JavaScript中等同于Java的UUID类的代码

huangapple go评论98阅读模式
英文:

Javascript equivalent of Java's UUID class

问题

在Java中,你可以像这样做:

  1. UUID id = UUID.fromString("eb66c416-4739-465b-9af3-9dc33ed8eef9");
  2. long msb = id.getMostSignificantBits();
  3. long lsb = id.getLeastSignificantBits();
  4. System.out.println(msb + ", " + lsb);
  5. // -1484283427208739237, -7281302710629372167
  6. System.out.println(new UUID(msb, lsb));
  7. // eb66c416-4739-465b-9af3-9dc33ed8eef9

这个相同的示例在另一个类似的问题中有引用,所以这是一个后续问题。在相关问题中,已经解决了从lsb和msb到字符串的问题,但我找不到字符串到msb和lsb的解决方案。

原始解决方案是:

  1. function toUuidString(lsb, msb) {
  2. return `${digits(msb >> 32n, 8n)}-${digits(msb >> 16n, 4n)}-${digits(
  3. msb,
  4. 4n
  5. )}-${digits(lsb >> 48n, 4n)}-${digits(lsb, 12n)}`
  6. }
  7. function digits(value, ds) {
  8. const hi = 1n << (ds * 4n)
  9. return (hi | (value & (hi - 1n))).toString(16).slice(1)
  10. }

现在我想要一个函数,接受一个字符串并返回msb和lsb。按照原始问题的路径,我已经找到了Java源代码并尝试进行等价的操作,如下所示:

  1. function fromString(name) {
  2. let components = name.split('-')
  3. if (components.length !== 5) {
  4. throw new Error(`Invalid UUID string: ${name}`)
  5. }
  6. for (let index = 0; index < 5; index++) {
  7. components[index] = `0x${components[index]}`
  8. }
  9. let mostSigBits = Number.parseInt(components[0], 16)
  10. mostSigBits <<= 16
  11. mostSigBits |= Number.parseInt(components[1], 16)
  12. mostSigBits <<= 16
  13. mostSigBits |= Number.parseInt(components[2], 16)
  14. let leastSigBits = Number.parseInt(components[3], 16)
  15. leastSigBits <<= 48
  16. leastSigBits |= Number.parseInt(components[4], 16)
  17. return {
  18. leastSigBits,
  19. mostSigBits,
  20. }
  21. }

然而,当我尝试像这样进行测试时:

  1. const originalUuid = 'eb66c416-4739-465b-9af3-9dc33ed8eef9'
  2. const parts = fromString(originalUuid)
  3. const newUUid = toUuidString(
  4. BigInt(parts.leastSigBits),
  5. BigInt(parts.mostSigBits)
  6. )
  7. console.log('Original', originalUuid)
  8. console.log('New', newUUid)

我得不到等价的UUIDs。它们有相同的部分,但一些部分丢失了:

  1. Original eb66c416-4739-465b-9af3-9dc33ed8eef9
  2. New 00000000-4739-465b-ffff-ffffbefbeef9

有什么想法出了问题吗?

英文:

In java, you can do something like

  1. UUID id = UUID.fromString(&quot;eb66c416-4739-465b-9af3-9dc33ed8eef9&quot;);
  2. long msb = id.getMostSignificantBits();
  3. long lsb = id.getLeastSignificantBits();
  4. System.out.println(msb + &quot;, &quot; + lsb);
  5. // -1484283427208739237, -7281302710629372167
  6. System.out.println(new UUID(msb, lsb));
  7. // eb66c416-4739-465b-9af3-9dc33ed8eef9

This same example is referenced in another question which is pretty similar, so this would be a follow up. While in the related question problem of lsb, msb -> string was solved, I cannot find solution for reverse problem, string -> msb, lsb

The original solution was

  1. function toUuidString(lsb, msb) {
  2. return `${digits(msb &gt;&gt; 32n, 8n)}-${digits(msb &gt;&gt; 16n, 4n)}-${digits(
  3. msb,
  4. 4n
  5. )}-${digits(lsb &gt;&gt; 48n, 4n)}-${digits(lsb, 12n)}`
  6. }
  7. function digits(value, ds) {
  8. const hi = 1n &lt;&lt; (ds * 4n)
  9. return (hi | (value &amp; (hi - 1n))).toString(16).slice(1)
  10. }

Now I'd like to have a function that takes in string and returns msb and lsb.
Following original questions' paths, I've discovered java source code and tried to do the equivalent, which would be:

  1. function fromString(name) {
  2. let components = name.split(&#39;-&#39;)
  3. if (components.length !== 5) {
  4. throw new Error(`Invalid UUID string: ${name}`)
  5. }
  6. for (let index = 0; index &lt; 5; index++) {
  7. components[index] = `0x${components[index]}`
  8. }
  9. let mostSigBits = Number.parseInt(components[0], 16)
  10. mostSigBits &lt;&lt;= 16
  11. mostSigBits |= Number.parseInt(components[1], 16)
  12. mostSigBits &lt;&lt;= 16
  13. mostSigBits |= Number.parseInt(components[2], 16)
  14. let leastSigBits = Number.parseInt(components[3], 16)
  15. leastSigBits &lt;&lt;= 48
  16. leastSigBits |= Number.parseInt(components[4], 16)
  17. return {
  18. leastSigBits,
  19. mostSigBits,
  20. }
  21. }

However, when I try to test this with something like:

  1. const originalUuid = &#39;eb66c416-4739-465b-9af3-9dc33ed8eef9&#39;
  2. const parts = fromString(originalUuid)
  3. const newUUid = toUuidString(
  4. BigInt(parts.leastSigBits),
  5. BigInt(parts.mostSigBits)
  6. )
  7. console.log(&#39;Original&#39;, originalUuid)
  8. console.log(&#39;New&#39;, newUUid)

I do not get equivalent uuids. They have equivalent parts but some parts are missing

  1. Original eb66c416-4739-465b-9af3-9dc33ed8eef9
  2. New 00000000-4739-465b-ffff-ffffbefbeef9

Any ideas what went wrong?

答案1

得分: 3

最终我找到了问题 - 两段代码并不严格等价,Java源代码声明了mostSigBits和leastSigBits为long,这在JavaScript中无法表示,因此我们需要使用BigInt。

总结一下我的问题和之前的问题,Java的UUID操作在JavaScript中的等价操作应为:

string -> msb, lsb

  1. function fromString(name) {
  2. let components = name.split('-')
  3. if (components.length !== 5) {
  4. throw new Error(`Invalid UUID string: ${name}`)
  5. }
  6. for (let index = 0; index < 5; index++) {
  7. components[index] = `0x${components[index]}`
  8. }
  9. let mostSigBits = BigInt(Number.parseInt(components[0], 16))
  10. mostSigBits <<= 16n
  11. mostSigBits |= BigInt(Number.parseInt(components[1], 16))
  12. mostSigBits <<= 16n
  13. mostSigBits |= BigInt(Number.parseInt(components[2], 16))
  14. let leastSigBits = BigInt(Number.parseInt(components[3], 16))
  15. leastSigBits <<= 48n
  16. leastSigBits |= BigInt(Number.parseInt(components[4], 16))
  17. return {
  18. leastSigBits,
  19. mostSigBits,
  20. }
  21. }

msb, lsb -> string (从引用的问题中)

  1. function toUuidString(lsb, msb) {
  2. return `${digits(msb >> 32n, 8n)}-${digits(msb >> 16n, 4n)}-${digits(
  3. msb,
  4. 4n
  5. )}-${digits(lsb >> 48n, 4n)}-${digits(lsb, 12n)}`
  6. }
  7. function digits(value, ds) {
  8. const hi = 1n << (ds * 4n)
  9. return (hi | (value & (hi - 1n))).toString(16).slice(1)
  10. }
英文:

Finally I found the problem - two codes were not strictly equivalent, java source code declared mostSigBits and leastSigBits as long, which cannot be represented in javascript, so we need to use BigInt.

To sum up my question and previous question, javascript equivalent for java's UUID operations would be:

string -> msb, lsb

  1. function fromString(name) {
  2. let components = name.split(&#39;-&#39;)
  3. if (components.length !== 5) {
  4. throw new Error(`Invalid UUID string: ${name}`)
  5. }
  6. for (let index = 0; index &lt; 5; index++) {
  7. components[index] = `0x${components[index]}`
  8. }
  9. let mostSigBits = BigInt(Number.parseInt(components[0], 16))
  10. mostSigBits &lt;&lt;= 16n
  11. mostSigBits |= BigInt(Number.parseInt(components[1], 16))
  12. mostSigBits &lt;&lt;= 16n
  13. mostSigBits |= BigInt(Number.parseInt(components[2], 16))
  14. let leastSigBits = BigInt(Number.parseInt(components[3], 16))
  15. leastSigBits &lt;&lt;= 48n
  16. leastSigBits |= BigInt(Number.parseInt(components[4], 16))
  17. return {
  18. leastSigBits,
  19. mostSigBits,
  20. }
  21. }

msb, lsb -> string (from the referenced question)

  1. function toUuidString(lsb, msb) {
  2. return `${digits(msb &gt;&gt; 32n, 8n)}-${digits(msb &gt;&gt; 16n, 4n)}-${digits(
  3. msb,
  4. 4n
  5. )}-${digits(lsb &gt;&gt; 48n, 4n)}-${digits(lsb, 12n)}`
  6. }
  7. function digits(value, ds) {
  8. const hi = 1n &lt;&lt; (ds * 4n)
  9. return (hi | (value &amp; (hi - 1n))).toString(16).slice(1)
  10. }

答案2

得分: 0

  1. JavaScript 如果 `(lsb, msb)->uuid` `uuid->(lsb, msb)` 都使用 BigInt那么您不需要考虑数字本身的位表示
  2. function toUuidString(lsb, msb) {
  3. return `${digits(msb >> 32n, 8n)}-${digits(msb >> 16n, 4n)}-${digits(msb, 4n)}-${digits(lsb >> 48n, 4n)}-${digits(lsb, 12n)}`
  4. }
  5. function digits(value, ds) {
  6. const hi = 1n << (ds * 4n)
  7. return (hi | (value & (hi - 1n))).toString(16).slice(1)
  8. }
  9. function fromString(name) {
  10. let components = name.split('-')
  11. if (components.length !== 5) {
  12. throw new Error(`Invalid UUID string: ${name}`)
  13. }
  14. for (let index = 0; index < 5; index++) {
  15. components[index] = `0x${components[index]}`
  16. }
  17. let mostSigBits = BigInt(`${components[0]}`)
  18. mostSigBits <<= 16n
  19. mostSigBits |= BigInt(`${components[1]}`)
  20. mostSigBits <<= 16n
  21. mostSigBits |= BigInt(`${components[2]}`)
  22. let leastSigBits = BigInt(`${components[3]}`)
  23. leastSigBits <<= 48n
  24. leastSigBits |= BigInt(`${components[4]}`)
  25. return {
  26. leastSigBits,
  27. mostSigBits,
  28. }
  29. }

但是,如果您从 Java 中直接获取字面值,比如从 getMostSignificantBitsgetLeastSignificantBits 获得值。那么您需要转换它,以确保其字节与 Java Long 表示字节相同(当值为负数时为 二进制补码)。

  1. // 假设 x 在有符号长整型值的范围内
  2. function unsignedBitShiftRight(x, n) {
  3. if (n === 0) { return x; }
  4. else if (x >= 0) { return x >> n; }
  5. // 转换为二进制补码,然后进行位移
  6. else { return (((1n << 64n) - 1n) ^ (- (x + 1n))) >> n; }
  7. }

>> 替换为 unsignedBitShiftRight

  1. // lsb & msb 取自 Java 的 getLeastSignificantBits 和 getMostSignificantBits
  2. function toUuidString(lsb, msb) {
  3. return `${digits(unsignedBitShiftRight(msb, 32n), 8n)}-${digits(unsignedBitShiftRight(msb, 16n), 4n)}-${digits(msb, 4n)}-${digits(unsignedBitShiftRight(lsb, 48n), 4n)}-${digits(lsb, 12n)}`
  4. }
  5. function digits(value, ds) {
  6. const hi = 1n << (ds * 4n)
  7. return (hi | (value & (hi - 1n))).toString(16).slice(1)
  8. }
  1. [1]: https://github.com/openjdk/jdk/blob/07734f6dde2b29574b6ef98eeb9e007d8801a3ea/src/java.base/share/classes/java/lang/Long.java#L448
  2. [2]: https://en.wikipedia.org/wiki/Signed_number_representations?#Two's_complement
  3. <details>
  4. <summary>英文:</summary>
  5. At JavaScript side, if the `(lsb, msb)-&gt;uuid` &amp; `uuid-&gt;(lsb, msb)` both use the BigInt, then you don&#39;t need to consider the bit representation of the number itself.
  6. function toUuidString(lsb, msb) {
  7. return `${digits(msb &gt;&gt; 32n, 8n)}-${
  8. digits(msb &gt;&gt; 16n, 4n)}-${
  9. digits(msb, 4n)}-${
  10. digits(lsb &gt;&gt; 48n, 4n)}-${digits(lsb, 12n)}`
  11. }
  12. function digits(value, ds) {
  13. const hi = 1n &lt;&lt; (ds * 4n)
  14. return (hi | (value &amp; (hi - 1n))).toString(16).slice(1)
  15. }
  16. function fromString(name) {
  17. let components = name.split(&#39;-&#39;)
  18. if (components.length !== 5) {
  19. throw new Error(`Invalid UUID string: ${name}`)
  20. }
  21. for (let index = 0; index &lt; 5; index++) {
  22. components[index] = `0x${components[index]}`
  23. }
  24. let mostSigBits = BigInt(`${components[0]}`)
  25. mostSigBits &lt;&lt;= 16n
  26. mostSigBits |= BigInt(`${components[1]}`)
  27. mostSigBits &lt;&lt;= 16n
  28. mostSigBits |= BigInt(`${components[2]}`)
  29. let leastSigBits = BigInt(`${components[3]}`)
  30. leastSigBits &lt;&lt;= 48n
  31. leastSigBits |= BigInt(`${components[4]}`)
  32. return {
  33. leastSigBits,
  34. mostSigBits,
  35. }
  36. }
  37. ----------
  38. But if you take the literal value from Java, say you got them from `getMostSignificantBits` and `getLeastSignificantBits`. Then you need to convert it to make sure its bytes are the same as what Java Long&#39;s representation bytes are (which are [two&#39;s complement][2] when the value is negative).
  39. // assume x under range of signed long value
  40. function unsignedBitShiftRight(x, n) {
  41. if (n === 0) { return x; }
  42. else if (x &gt;= 0) { return x &gt;&gt; n; }
  43. // convert to two&#39;s complement and then do the shifting
  44. else { return (((1n &lt;&lt; 64n) - 1n) ^ (- (x + 1n))) &gt;&gt; n; }
  45. }
  46. Replace the `&gt;&gt;` with `unsignedBitShiftRight`:
  47. // lsb &amp; msb taken from Java&#39;s getLeastSignificantBits &amp; getMostSignificantBits
  48. function toUuidString(lsb, msb) {
  49. return `${
  50. digits(unsignedBitShiftRight(msb, 32n), 8n)}-${
  51. digits(unsignedBitShiftRight(msb, 16n), 4n)}-${
  52. digits(msb, 4n)}-${
  53. digits(unsignedBitShiftRight(lsb, 48n), 4n)}-${
  54. digits(lsb, 12n)}`
  55. }
  56. function digits(value, ds) {
  57. const hi = 1n &lt;&lt; (ds * 4n)
  58. return (hi | (value &amp; (hi - 1n))).toString(16).slice(1)
  59. }
  60. [1]: https://github.com/openjdk/jdk/blob/07734f6dde2b29574b6ef98eeb9e007d8801a3ea/src/java.base/share/classes/java/lang/Long.java#L448
  61. [2]: https://en.wikipedia.org/wiki/Signed_number_representations?#Two&#39;s_complement
  62. </details>

huangapple
  • 本文由 发表于 2023年2月8日 23:11:29
  • 转载请务必保留本文链接:https://go.coder-hub.com/75387792.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定