此功能应如何从 PHP 正确移植到 Javascript
How this function should be properly ported from PHP to Javascript
我正在做一个项目,我需要一个只能从 PHP 项目获得的编码:
public static function writeVarLong(int $v) : string{
return self::writeUnsignedVarLong(($v << 1) ^ ($v >> 63));
}
public static function writeUnsignedVarLong(int $value) : string{
$buf = "";
for($i = 0; $i < 10; ++$i){
if(($value >> 7) !== 0){
$buf .= chr($value | 0x80); //Let chr() take the last byte of this, it's faster than adding another & 0x7f.
}else{
$buf .= chr($value & 0x7f);
return $buf;
}
$value = (($value >> 7) & (PHP_INT_MAX >> 6)); //PHP really needs a logical right-shift operator
}
throw new InvalidArgumentException("Value too large to be encoded as a VarLong");
}
这是我的 Javascript 实现:
writeVarLong(v) {
return this.writeUnsignedVarLong((v << 1) ^ (v >> 63))
}
writeUnsignedVarLong(v) {
for (let i = 0; i < 10; i++) {
if ((v >> 7) !== 0) {
this.writeByte(v | 0x80)
} else {
this.writeByte(v & 0x7f)
break
}
v >>= 7
}
}
当我运行一个简单的测试时,问题就来了,我实例化了两个二进制流,并在它们上面写了一个值,问题是输出不同。
这是 PHP 测试和输出:
$stream = new NetworkBinaryStream();
$stream->putVarLong(422212465606656);
var_dump(bin2hex($stream->buffer));
// Output: 8080c2808080c001
这是 Javascript 测试和输出:
let stream = new PacketBinaryStream()
stream.writeVarLong(422212465606656)
console.log(stream.buffer)
// Output: 80 80 42
我们可以看到输出是不同的
完整文件:
这些方法遇到了按位运算符仅在 32 位中工作的问题,因此 64 位数字在序列化之前被截断了。
您可以使用 BigInt 解决此问题。
const BinaryStream = require('jsbinaryutils')
class PacketBinaryStream extends BinaryStream {
writeVarLong(v) {
let bi = BigInt(v);
return this.writeUnsignedVarLong((bi << 1n) ^ (bi >> 63n))
}
writeUnsignedVarLong(v) {
let bi = BigInt(v);
for (let i = 0; i < 10; i++) {
if ((bi >> 7n) !== 0n) {
this.writeByte(Number((bi | 0x80n)));
} else {
this.writeByte(Number((bi & 0x7fn)));
break
}
bi >>= 7n
}
}
}
let toWrite = 422212465606656;
let stream = new PacketBinaryStream();
stream.writeVarLong(toWrite);
console.log(stream.buffer);
// Output: <Buffer 80 80 c2 80 80 80 c0 01>
我正在做一个项目,我需要一个只能从 PHP 项目获得的编码:
public static function writeVarLong(int $v) : string{
return self::writeUnsignedVarLong(($v << 1) ^ ($v >> 63));
}
public static function writeUnsignedVarLong(int $value) : string{
$buf = "";
for($i = 0; $i < 10; ++$i){
if(($value >> 7) !== 0){
$buf .= chr($value | 0x80); //Let chr() take the last byte of this, it's faster than adding another & 0x7f.
}else{
$buf .= chr($value & 0x7f);
return $buf;
}
$value = (($value >> 7) & (PHP_INT_MAX >> 6)); //PHP really needs a logical right-shift operator
}
throw new InvalidArgumentException("Value too large to be encoded as a VarLong");
}
这是我的 Javascript 实现:
writeVarLong(v) {
return this.writeUnsignedVarLong((v << 1) ^ (v >> 63))
}
writeUnsignedVarLong(v) {
for (let i = 0; i < 10; i++) {
if ((v >> 7) !== 0) {
this.writeByte(v | 0x80)
} else {
this.writeByte(v & 0x7f)
break
}
v >>= 7
}
}
当我运行一个简单的测试时,问题就来了,我实例化了两个二进制流,并在它们上面写了一个值,问题是输出不同。
这是 PHP 测试和输出:
$stream = new NetworkBinaryStream();
$stream->putVarLong(422212465606656);
var_dump(bin2hex($stream->buffer));
// Output: 8080c2808080c001
这是 Javascript 测试和输出:
let stream = new PacketBinaryStream()
stream.writeVarLong(422212465606656)
console.log(stream.buffer)
// Output: 80 80 42
我们可以看到输出是不同的
完整文件:
这些方法遇到了按位运算符仅在 32 位中工作的问题,因此 64 位数字在序列化之前被截断了。
您可以使用 BigInt 解决此问题。
const BinaryStream = require('jsbinaryutils')
class PacketBinaryStream extends BinaryStream {
writeVarLong(v) {
let bi = BigInt(v);
return this.writeUnsignedVarLong((bi << 1n) ^ (bi >> 63n))
}
writeUnsignedVarLong(v) {
let bi = BigInt(v);
for (let i = 0; i < 10; i++) {
if ((bi >> 7n) !== 0n) {
this.writeByte(Number((bi | 0x80n)));
} else {
this.writeByte(Number((bi & 0x7fn)));
break
}
bi >>= 7n
}
}
}
let toWrite = 422212465606656;
let stream = new PacketBinaryStream();
stream.writeVarLong(toWrite);
console.log(stream.buffer);
// Output: <Buffer 80 80 c2 80 80 80 c0 01>