I am working on a project where I need an encoding that I can only get from a PHP project:
JavaScript
x
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");
}
Here is my Javascript implementation:
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
}
}
The problem comes when I run a simple test, I instantiate the two binary streams and I wrote a value on them, the problem is that output is different.
Here is the PHP test & output:
JavaScript
$stream = new NetworkBinaryStream();
$stream->putVarLong(422212465606656);
var_dump(bin2hex($stream->buffer));
// Output: 8080c2808080c001
Here is the Javascript test & output:
JavaScript
let stream = new PacketBinaryStream()
stream.writeVarLong(422212465606656)
console.log(stream.buffer)
// Output: 80 80 42
As we can see outputs are different
Complete files:
PHP => https://github.com/pmmp/BinaryUtils/blob/master/src/Binary.php
Javascript => https://github.com/HerryYT/JSBinaryUtils/blob/master/binarystream.js
Advertisement
Answer
These methods are hitting an issue where the bitwise operators are only working in 32-bit, so the 64-bit numbers are being truncated before your serialisation.
You can work around this with BigInt.
JavaScript
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>