I am working on a project where I need an encoding that I can only get from a PHP project:
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:
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:
$stream = new NetworkBinaryStream(); $stream->putVarLong(422212465606656); var_dump(bin2hex($stream->buffer)); // Output: 8080c2808080c001
Here is the Javascript test & output:
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.
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>