英文:
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("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
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 >> 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)
}
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('-')
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,
}
}
However, when I try to test this with something like:
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)
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('-')
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 (from the referenced question)
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)
}
答案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 中直接获取字面值,比如从 getMostSignificantBits
和 getLeastSignificantBits
获得值。那么您需要转换它,以确保其字节与 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)->uuid` & `uuid->(lsb, msb)` both use the BigInt, then you don't need to consider the bit representation of the number itself.
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,
}
}
----------
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's representation bytes are (which are [two'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 >= 0) { return x >> n; }
// convert to two's complement and then do the shifting
else { return (((1n << 64n) - 1n) ^ (- (x + 1n))) >> n; }
}
Replace the `>>` with `unsignedBitShiftRight`:
// lsb & msb taken from Java's 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>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论