为了在使用协议缓冲区传递uuid时节省空间,我们使用MSB/LSB表示发送它们,两个64位long
值
message Uuid {
sfixed64 msb = 1;
sfixed64 lsb = 2;
}
这些在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
然而,由于JavaScript的number
只能达到253- 1,我无法在我的TypeScript客户端中将MSB/LSB格式转换回string
。这可能吗?
看JavaUUID
的toString()
方法的启发,
public String toString() {
return (digits(mostSigBits >> 32, 8) + "-" +
digits(mostSigBits >> 16, 4) + "-" +
digits(mostSigBits, 4) + "-" +
digits(leastSigBits >> 48, 4) + "-" +
digits(leastSigBits, 12));
}
private static String digits(long val, int digits) {
long hi = 1L << (digits * 4);
return Long.toHexString(hi | (val & (hi - 1))).substring(1);
}
我们可以使用BigInt
做同样的事情。这里假设Node 10.8+(在18.14.0上测试),TypeScript的目标是ES2020+,并且与此浏览器兼容。
注意:如果您得到"BigInt字面量不可用…"将所有以n
结尾的字面值用BigInt
代替(例如,用BigInt(32)
代替32n
)。
function uuidSigBitsToStr({ lsb, msb }: { lsb: bigint; msb: bigint }): string {
return `${digits(msb >> 32n, 8n)}-${digits(msb >> 16n, 4n)}-${digits(
msb,
4n
)}-${digits(lsb >> 48n, 4n)}-${digits(lsb, 12n)}`;
}
function digits(val: bigint, ds: bigint): string {
const hi = 1n << (ds * 4n);
return (hi | (val & (hi - 1n))).toString(16).substring(1);
}
和一个示例测试,注意msb/lsb作为string
s传递给BigInt
,
it('converts UUID from msb/lsb to string', () => {
expect(
toUuidString(
BigInt('-1160168401362026442'),
BigInt('-6694969989912915968')
)
).toEqual('a316b044-0157-1000-efe6-40fc5d2f0036');
});
最后一部分是协议缓冲区。默认情况下,google-protobuf
对64位float
和int
的值使用number
,导致Number.MAX_VALUE
以上溢出或253- 1。为了避免这种情况,在64位字段上使用jstype
注释
message Uuid {
sfixed64 msb = 1 [jstype = JS_STRING];
sfixed64 lsb = 2 [jstype = JS_STRING];
}
2023更新:反向功能,从UUID字符串转换为有效位
function uuidStrToSigBits(uuid: string) {
const invalidError = () => new Error(`Invalid UUID string: '${uuid}'`);
if (uuid == null || typeof uuid !== "string") throw invalidError();
const parts = uuid.split("-").map((p) => `0x${p}`);
if (parts.length !== 5) throw invalidError();
return {
lsb: (hexStrToBigInt(parts[3]) << 48n) | hexStrToBigInt(parts[4]),
msb:
(hexStrToBigInt(parts[0]) << 32n) |
(hexStrToBigInt(parts[1]) << 16n) |
hexStrToBigInt(parts[2]),
};
}
function hexStrToBigInt(hex: string): bigint {
return BigInt(Number.parseInt(hex, 16));
}
这是一个往返测试,
const uuidSigBits = uuidStrToSigBits("7680fc63-1f58-412a-9bde-c5a9de1e0ce9");
const uuidStr = uuidSigBitsToStr(uuidSigBits);
console.log(uuidStr);
// => 7680fc63-1f58-412a-9bde-c5a9de1e0ce9