首页 > 解决方案 > 为什么 V8(一个 javascript 模块)反序列化两个 2 个不同的十六进制字符串但带来 2 个相同的对象?

问题描述

我试图弄清楚为什么会发生这种情况。

const v8 = require('v8')

let stringa = 'ff0d6f220762616c616e63655a200070824fdc89df747141410000000000220a6465706c6f7965644279220673797374656d220773746f726167656f220a63616e646964617465736f222c49364e4d6f2b4b5a3531634b2b39626f6543554f716e6570724361723566727752746771746c493350596b3d6f2205626c6f636b4e000000000000244022076465706f7369745a20000088b116afe3b5020000000000000022046e616d65220d4e65772056616c696461746f7222086f70657261746f72222b74676c6331613979667337653765763270376a6164396b71757774767870637a6e6e65633432327564726b7b04222c576b5a6c346470454666797436585a506662433270766f5777424d6a314f497161794958304b47714a654d3d6f2205626c6f636b4e000000000000000022076465706f7369745a20000088b116afe3b5020000000000000022046e616d65221147656e657369732056616c696461746f7222086f70657261746f72222b74676c6331613979667337653765763270376a6164396b71757774767870637a6e6e65633432327564726b2206766f746572736f222b74676c6331613979667337653765763270376a6164396b71757774767870637a6e6e65633432327564726b5a200000e8c69583abe311000000000000007b017b057b02220877697468647261776f222b74676c6331613979667337653765763270376a6164396b71757774767870637a6e6e65633432327564726b6f49005a100000dcce86b42ad07b017b017b02220673797374656d547b04'
let a = v8.deserialize(Buffer.from(stringa, 'hex'))
console.dir(a, {depth: null})

let stringb = 'ff0d6f220762616c616e63655a200070824fdc89df747141410000000000220a6465706c6f7965644279220673797374656d220773746f726167656f220a63616e646964617465736f222c49364e4d6f2b4b5a3531634b2b39626f6543554f716e6570724361723566727752746771746c493350596b3d6f2205626c6f636b491422076465706f7369745a20000088b116afe3b5020000000000000022046e616d65220d4e65772056616c696461746f7222086f70657261746f72222b74676c6331613979667337653765763270376a6164396b71757774767870637a6e6e65633432327564726b7b04222c576b5a6c346470454666797436585a506662433270766f5777424d6a314f497161794958304b47714a654d3d6f2205626c6f636b490022076465706f7369745a20000088b116afe3b5020000000000000022046e616d65221147656e657369732056616c696461746f7222086f70657261746f72222b74676c6331613979667337653765763270376a6164396b71757774767870637a6e6e65633432327564726b2206766f746572736f222b74676c6331613979667337653765763270376a6164396b71757774767870637a6e6e65633432327564726b5a200000e8c69583abe311000000000000007b017b057b02220877697468647261776f222b74676c6331613979667337653765763270376a6164396b71757774767870637a6e6e65633432327564726b6f49005a100000dcce86b42ad07b017b017b02220673797374656d547b04'
let b = v8.deserialize(Buffer.from(stringb, 'hex'))
console.dir(b, {depth: null})

stringa和之间明显不同stringb。但是当我打印两个对象ab. 我意识到他们是一样的。

谁能向我解释为什么?提前非常感谢大家。

标签: javascriptserializationhexdeserializationv8

解决方案


SerializationTag定义的帮助下,手动解码这些字符串并不难,例如:

ff 0d // non-legacy version: 13
6f // object start
22 07 // one-byte string, length=7
62 61 6c 61 6e 63 65 // "balance"
5a 20 // bigint, bitfield: sign=0 length=16
00 70 82 4f dc 89 df 74 71 41 41 00 00 00 00 00 
22 0a // one-byte string, length=10
64 65 70 6c 6f 79 65 64 42 79 // "deployedBy"
22 06 // one-byte string, length=6
73 79 73 74 65 6d // "system"
...

当你这样做的时间足够长时,你最终会看到它的stringa用途:

22 05 // one-byte string, length=5
62 6c 6f 63 6b // "block"
4e // double
0000000000002440 // little-endian encoding of "10.0" as an IEEE754 double

stringb使用:

22 05 // one-byte string, length=5
62 6c 6f 63 6b // "block"
49 // int32
14 // "zig-zag" encoding of "10"

所以当你将这些字符串反序列化为 JavaScript 对象时,那么整数10和双精度当然10.0是无法区分的,因为在 JS 中,两者都是Numbers。


退后一步:不管这里的具体解释如何,依赖您无法控制的序列化格式的特定行为并不是一个好主意。它可能会发生不可预测的变化。V8 的序列化/反序列化 API 在底层所做的是一个内部实现细节(这也是没有关于它的文档的原因;你必须阅读源代码才能弄清楚),它可以改变。而且,事实上,它确实改变了!该字符串的序列化格式版本为 13,这意味着在此之前还有 12 个其他版本,并且可以随时推出新版本。

即使除了新的序列化格式版本之外,我还能想到几个其他原因,为什么不同的编码可以反序列化为外观相同的 JS 对象(例如字符串编码、NaN 模式、varint 或 BigInt 数据中的前导零、可选的可忽略字节......) .

如果您需要对对象的序列化格式做出任何保证,您应该实现自己的序列化算法,以便确保这些保证确实成立。


推荐阅读