你的方法有两个问题。
-
无法解析。
例如,即使您只知道自己有两个数字,
字节 12 34 56 7816 可以指 1216 和 34567816。
字节 12 34 56 7816 可以指 123416 和 567816。
字节 12 34 56 7816 可以指 12345616 和 7816。
您可以使用一些外部方法来识别编码数字的长度,但这会使部分或全部节省无效。
-
这是模棱两可的。
例如,
Bytes FD D516 可以引用 6498110(Bytes 00 00 FD D516 作为 int32)。
Bytes FD D516 可以引用 -55510(Bytes FF FF FD D516 作为 int32)。
一种解决方案是使用长度前缀(如 UTF-8)。
-2^13..2^13-1 2 bytes 00xx xxxx xxxx xxxx
-2^21..2^21-1 3 bytes 01xx xxxx xxxx xxxx xxxx xxxx
-2^29..2^29-1 4 bytes 10xx xxxx xxxx xxxx xxxx xxxx xxxx xxxx
-2^31..2^31-1 5 bytes 1100 0000 xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx
最佳方案将取决于您的数字分布。
上述方案的打包/编码函数可以写成如下:
sub pack_vint32 {
my $n = shift;
my $nn = $n >= 0 ? $n : ~$n;
return substr(pack('L>', ($n & 0x3FFF ) | 0x0000 ), -2) if !($nn & ~0x1FFF);
return substr(pack('L>', ($n & 0x3FFFFF ) | 0x400000 ), -3) if !($nn & ~0x1FFFFF);
return substr(pack('L>', ($n & 0x3FFFFFFF) | 0x80000000), -4) if !($nn & ~0x1FFFFFFF);
return "\xC0".pack('L>', $n);
}
上述方案的解包/解码函数可以写成如下:
sub unpack_vint32 {
for (shift) {
if (/^[\x00-\x3F]/) {
return if length() < 2;
my $n = unpack('L>', "\x00\x00".substr($_, 0, 2, '')) & 0x3FFF;
$n -= 0x4000 if $n & 0x2000;
return $n;
}
elsif (/^[\x40-\x7F]/) {
return if length() < 3;
my $n = unpack('L>', "\x00".substr($_, 0, 3, '')) & 0x3FFFFF;
$n -= 0x400000 if $n & 0x200000;
return $n;
}
elsif (/^[\x80-\xBF]/) {
return if length() < 4;
my $n = unpack('L>', substr($_, 0, 4, '')) & 0x3FFFFFFF;
$n -= 0x40000000 if $n & 0x20000000;
return $n;
}
elsif (/^\xC0/) {
return if length() < 5;
return unpack('xl>', substr($_, 0, 5, ''));
}
elsif (length() == 0) {
return;
}
}
croak("Bad data");
}
测试:
my $s =
join '',
map { pack_vint32($_) }
map { $_, -$_ }
130, 555, 0x12, 0x345678, 0x12345678;
say length($s);
say sprintf("%v02X", $s);
while ( my ($n) = unpack_vint32($s) ) {
say $n;
}
croak("Bad data") if length($s);
输出:
28
00.82.3F.7E.02.2B.3D.D5.00.12.3F.EE.80.34.56.78.BF.CB.A9.88.92.34.56.78.AD.CB.A9.88
----- ----- ----- ----- ----- ----- ----------- ----------- ----------- -----------
130 | | | | | | | | |
-130 ---+ | | | | | | | |
555 ----------+ | | | | | | |
-555 ---------------+ | | | | | |
18 -----------------------+ | | | | |
-18 ----------------------------+ | | | |
3430008 ---------------------------------+ | | |
-3430008 --------------------------------------------+ | |
305419896 -------------------------------------------------------+ |
-305419896 ------------------------------------------------------------------+