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

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

Javascript equivalent of Java's UUID class

问题

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

UUID id = UUID.fromString("eb66c416-4739-465b-9af3-9dc33ed8eef9");
long msb = id.getMostSignificantBits();
long lsb = id.getLeastSignificantBits();
System.out.println(msb + ", " + lsb);
  // -1484283427208739237, -7281302710629372167

System.out.println(new UUID(msb, lsb));
  // eb66c416-4739-465b-9af3-9dc33ed8eef9

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

原始解决方案是:

function toUuidString(lsb, msb) {
    return `${digits(msb >> 32n, 8n)}-${digits(msb >> 16n, 4n)}-${digits(
        msb,
        4n
    )}-${digits(lsb >> 48n, 4n)}-${digits(lsb, 12n)}`
}

function digits(value, ds) {
    const hi = 1n << (ds * 4n)

    return (hi | (value & (hi - 1n))).toString(16).slice(1)
}

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

function fromString(name) {
    let components = name.split('-')

    if (components.length !== 5) {
        throw new Error(`Invalid UUID string: ${name}`)
    }

    for (let index = 0; index < 5; index++) {
        components[index] = `0x${components[index]}`
    }

    let mostSigBits = Number.parseInt(components[0], 16)
    mostSigBits <<= 16
    mostSigBits |= Number.parseInt(components[1], 16)
    mostSigBits <<= 16
    mostSigBits |= Number.parseInt(components[2], 16)

    let leastSigBits = Number.parseInt(components[3], 16)
    leastSigBits <<= 48
    leastSigBits |= Number.parseInt(components[4], 16)

    return {
        leastSigBits,
        mostSigBits,
    }
}

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

const originalUuid = 'eb66c416-4739-465b-9af3-9dc33ed8eef9'
const parts = fromString(originalUuid)

const newUUid = toUuidString(
    BigInt(parts.leastSigBits),
    BigInt(parts.mostSigBits)
)

console.log('Original', originalUuid)
console.log('New', newUUid)

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

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

有什么想法出了问题吗?

英文:

In java, you can do something like

UUID id = UUID.fromString(&quot;eb66c416-4739-465b-9af3-9dc33ed8eef9&quot;);
long msb = id.getMostSignificantBits();
long lsb = id.getLeastSignificantBits();
System.out.println(msb + &quot;, &quot; + lsb);
  // -1484283427208739237, -7281302710629372167

System.out.println(new UUID(msb, lsb));
  // 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

function toUuidString(lsb, msb) {
    return `${digits(msb &gt;&gt; 32n, 8n)}-${digits(msb &gt;&gt; 16n, 4n)}-${digits(
        msb,
        4n
    )}-${digits(lsb &gt;&gt; 48n, 4n)}-${digits(lsb, 12n)}`
}

function digits(value, ds) {
    const hi = 1n &lt;&lt; (ds * 4n)

    return (hi | (value &amp; (hi - 1n))).toString(16).slice(1)
}

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:

function fromString(name) {
    let components = name.split(&#39;-&#39;)

    if (components.length !== 5) {
        throw new Error(`Invalid UUID string: ${name}`)
    }

    for (let index = 0; index &lt; 5; index++) {
        components[index] = `0x${components[index]}`
    }

    let mostSigBits = Number.parseInt(components[0], 16)
    mostSigBits &lt;&lt;= 16
    mostSigBits |= Number.parseInt(components[1], 16)
    mostSigBits &lt;&lt;= 16
    mostSigBits |= Number.parseInt(components[2], 16)

    let leastSigBits = Number.parseInt(components[3], 16)
    leastSigBits &lt;&lt;= 48
    leastSigBits |= Number.parseInt(components[4], 16)

    return {
        leastSigBits,
        mostSigBits,
    }
}

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

const originalUuid = &#39;eb66c416-4739-465b-9af3-9dc33ed8eef9&#39;
const parts = fromString(originalUuid)

const newUUid = toUuidString(
    BigInt(parts.leastSigBits),
    BigInt(parts.mostSigBits)
)

console.log(&#39;Original&#39;, originalUuid)
console.log(&#39;New&#39;, newUUid)

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

Original eb66c416-4739-465b-9af3-9dc33ed8eef9
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

function fromString(name) {
    let components = name.split('-')

    if (components.length !== 5) {
        throw new Error(`Invalid UUID string: ${name}`)
    }

    for (let index = 0; index < 5; index++) {
        components[index] = `0x${components[index]}`
    }

    let mostSigBits = BigInt(Number.parseInt(components[0], 16))
    mostSigBits <<= 16n
    mostSigBits |= BigInt(Number.parseInt(components[1], 16))
    mostSigBits <<= 16n
    mostSigBits |= BigInt(Number.parseInt(components[2], 16))

    let leastSigBits = BigInt(Number.parseInt(components[3], 16))
    leastSigBits <<= 48n
    leastSigBits |= BigInt(Number.parseInt(components[4], 16))

    return {
        leastSigBits,
        mostSigBits,
    }
}

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

function toUuidString(lsb, msb) {
    return `${digits(msb >> 32n, 8n)}-${digits(msb >> 16n, 4n)}-${digits(
        msb,
        4n
    )}-${digits(lsb >> 48n, 4n)}-${digits(lsb, 12n)}`
}

function digits(value, ds) {
    const hi = 1n << (ds * 4n)

    return (hi | (value & (hi - 1n))).toString(16).slice(1)
}
英文:

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

function fromString(name) {
    let components = name.split(&#39;-&#39;)

    if (components.length !== 5) {
        throw new Error(`Invalid UUID string: ${name}`)
    }

    for (let index = 0; index &lt; 5; index++) {
        components[index] = `0x${components[index]}`
    }

    let mostSigBits = BigInt(Number.parseInt(components[0], 16))
    mostSigBits &lt;&lt;= 16n
    mostSigBits |= BigInt(Number.parseInt(components[1], 16))
    mostSigBits &lt;&lt;= 16n
    mostSigBits |= BigInt(Number.parseInt(components[2], 16))

    let leastSigBits = BigInt(Number.parseInt(components[3], 16))
    leastSigBits &lt;&lt;= 48n
    leastSigBits |= BigInt(Number.parseInt(components[4], 16))

    return {
        leastSigBits,
        mostSigBits,
    }
}

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

function toUuidString(lsb, msb) {
    return `${digits(msb &gt;&gt; 32n, 8n)}-${digits(msb &gt;&gt; 16n, 4n)}-${digits(
        msb,
        4n
    )}-${digits(lsb &gt;&gt; 48n, 4n)}-${digits(lsb, 12n)}`
}

function digits(value, ds) {
    const hi = 1n &lt;&lt; (ds * 4n)

    return (hi | (value &amp; (hi - 1n))).toString(16).slice(1)
}

答案2

得分: 0

 JavaScript 如果 `(lsb, msb)->uuid`  `uuid->(lsb, msb)` 都使用 BigInt那么您不需要考虑数字本身的位表示

    function toUuidString(lsb, msb) {
      return `${digits(msb >> 32n, 8n)}-${digits(msb >> 16n, 4n)}-${digits(msb, 4n)}-${digits(lsb >> 48n, 4n)}-${digits(lsb, 12n)}`
    }
    
    function digits(value, ds) {
      const hi = 1n << (ds * 4n)
      return (hi | (value & (hi - 1n))).toString(16).slice(1)
    }
    
    
    function fromString(name) {
      let components = name.split('-')
    
      if (components.length !== 5) {
        throw new Error(`Invalid UUID string: ${name}`)
      }
    
      for (let index = 0; index < 5; index++) {
        components[index] = `0x${components[index]}`
      }
    
      let mostSigBits = BigInt(`${components[0]}`)
      mostSigBits <<= 16n
      mostSigBits |= BigInt(`${components[1]}`)
      mostSigBits <<= 16n
      mostSigBits |= BigInt(`${components[2]}`)
    
      let leastSigBits = BigInt(`${components[3]}`)
      leastSigBits <<= 48n
      leastSigBits |= BigInt(`${components[4]}`)
    
      return {
        leastSigBits,
        mostSigBits,
      }
    }

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

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

>> 替换为 unsignedBitShiftRight

// lsb & msb 取自 Java 的 getLeastSignificantBits 和 getMostSignificantBits
function toUuidString(lsb, msb) {
return `${digits(unsignedBitShiftRight(msb, 32n), 8n)}-${digits(unsignedBitShiftRight(msb, 16n), 4n)}-${digits(msb, 4n)}-${digits(unsignedBitShiftRight(lsb, 48n), 4n)}-${digits(lsb, 12n)}`
}
function digits(value, ds) {
const hi = 1n << (ds * 4n)
return (hi | (value & (hi - 1n))).toString(16).slice(1)
}

[1]: https://github.com/openjdk/jdk/blob/07734f6dde2b29574b6ef98eeb9e007d8801a3ea/src/java.base/share/classes/java/lang/Long.java#L448
[2]: https://en.wikipedia.org/wiki/Signed_number_representations?#Two's_complement
<details>
<summary>英文:</summary>
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.
function toUuidString(lsb, msb) {
return `${digits(msb &gt;&gt; 32n, 8n)}-${
digits(msb &gt;&gt; 16n, 4n)}-${
digits(msb, 4n)}-${
digits(lsb &gt;&gt; 48n, 4n)}-${digits(lsb, 12n)}`
}
function digits(value, ds) {
const hi = 1n &lt;&lt; (ds * 4n)
return (hi | (value &amp; (hi - 1n))).toString(16).slice(1)
}
function fromString(name) {
let components = name.split(&#39;-&#39;)
if (components.length !== 5) {
throw new Error(`Invalid UUID string: ${name}`)
}
for (let index = 0; index &lt; 5; index++) {
components[index] = `0x${components[index]}`
}
let mostSigBits = BigInt(`${components[0]}`)
mostSigBits &lt;&lt;= 16n
mostSigBits |= BigInt(`${components[1]}`)
mostSigBits &lt;&lt;= 16n
mostSigBits |= BigInt(`${components[2]}`)
let leastSigBits = BigInt(`${components[3]}`)
leastSigBits &lt;&lt;= 48n
leastSigBits |= BigInt(`${components[4]}`)
return {
leastSigBits,
mostSigBits,
}
}
----------
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).
// assume x under range of signed long value
function unsignedBitShiftRight(x, n) {
if (n === 0) { return x; }
else if (x &gt;= 0) { return x &gt;&gt; n; }
// convert to two&#39;s complement and then do the shifting
else { return (((1n &lt;&lt; 64n) - 1n) ^ (- (x + 1n))) &gt;&gt; n; }
}
Replace the `&gt;&gt;` with `unsignedBitShiftRight`:
// lsb &amp; msb taken from Java&#39;s getLeastSignificantBits &amp; getMostSignificantBits
function toUuidString(lsb, msb) {
return `${
digits(unsignedBitShiftRight(msb, 32n), 8n)}-${
digits(unsignedBitShiftRight(msb, 16n), 4n)}-${
digits(msb, 4n)}-${
digits(unsignedBitShiftRight(lsb, 48n), 4n)}-${
digits(lsb, 12n)}`
}
function digits(value, ds) {
const hi = 1n &lt;&lt; (ds * 4n)
return (hi | (value &amp; (hi - 1n))).toString(16).slice(1)
}
[1]: https://github.com/openjdk/jdk/blob/07734f6dde2b29574b6ef98eeb9e007d8801a3ea/src/java.base/share/classes/java/lang/Long.java#L448
[2]: https://en.wikipedia.org/wiki/Signed_number_representations?#Two&#39;s_complement
</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:

确定