22declare(strict_types=1);
29use InvalidArgumentException;
33use
function preg_replace;
42 private const SIZEOF_SHORT = 2;
43 private const SIZEOF_INT = 4;
44 private const SIZEOF_LONG = 8;
46 private const SIZEOF_FLOAT = 4;
47 private const SIZEOF_DOUBLE = 8;
49 public static function signByte(
int $value) :
int{
50 return $value << 56 >> 56;
53 public static function unsignByte(
int $value) :
int{
57 public static function signShort(
int $value) :
int{
58 return $value << 48 >> 48;
61 public static function unsignShort(
int $value) :
int{
62 return $value & 0xffff;
65 public static function signInt(
int $value) :
int{
66 return $value << 32 >> 32;
69 public static function unsignInt(
int $value) :
int{
70 return $value & 0xffffffff;
73 public static function flipShortEndianness(
int $value) :
int{
77 public static function flipIntEndianness(
int $value) :
int{
81 public static function flipLongEndianness(
int $value) :
int{
89 private static function safeUnpack(
string $formatCode,
string $bytes,
int $needLength) : array{
90 $haveLength = strlen($bytes);
91 if($haveLength < $needLength){
95 $result = unpack($formatCode, $bytes);
96 if($result ===
false){
98 throw new \AssertionError(
"unpack() failed for unknown reason");
107 return $b[0] !==
"\x00";
114 return $b ?
"\x01" :
"\x00";
138 return self::signByte(ord($c[0]));
154 return self::safeUnpack(
"n", $str, self::SIZEOF_SHORT)[1];
163 return self::signShort(self::safeUnpack(
"n", $str, self::SIZEOF_SHORT)[1]);
170 return pack(
"n", $value);
179 return self::safeUnpack(
"v", $str, self::SIZEOF_SHORT)[1];
188 return self::signShort(self::safeUnpack(
"v", $str, self::SIZEOF_SHORT)[1]);
195 return pack(
"v", $value);
204 return self::safeUnpack(
"N",
"\x00" . $str, self::SIZEOF_INT)[1];
211 return substr(pack(
"N", $value), 1);
220 return self::safeUnpack(
"V", $str .
"\x00", self::SIZEOF_INT)[1];
227 return substr(pack(
"V", $value), 0, -1);
235 public static function readInt(
string $str) : int{
236 return self::signInt(self::safeUnpack(
"N", $str, self::SIZEOF_INT)[1]);
242 public static function writeInt(
int $value) : string{
243 return pack(
"N", $value);
251 public static function readLInt(
string $str) : int{
252 return self::signInt(self::safeUnpack(
"V", $str, self::SIZEOF_INT)[1]);
259 return pack(
"V", $value);
268 return self::safeUnpack(
"G", $str, self::SIZEOF_FLOAT)[1];
277 return round(self::readFloat($str), $accuracy);
284 return pack(
"G", $value);
293 return self::safeUnpack(
"g", $str, self::SIZEOF_FLOAT)[1];
302 return round(self::readLFloat($str), $accuracy);
309 return pack(
"g", $value);
316 return preg_replace(
"/(\\.\\d+?)0+$/",
"$1", sprintf(
"%F", $value));
325 return self::safeUnpack(
"E", $str, self::SIZEOF_DOUBLE)[1];
332 return pack(
"E", $value);
341 return self::safeUnpack(
"e", $str, self::SIZEOF_DOUBLE)[1];
348 return pack(
"e", $value);
356 public static function readLong(
string $str) : int{
357 return self::safeUnpack(
"J", $str, self::SIZEOF_LONG)[1];
364 return pack(
"J", $value);
373 return self::safeUnpack(
"P", $str, self::SIZEOF_LONG)[1];
380 return pack(
"P", $value);
390 public static function readVarInt(
string $buffer,
int &$offset) : int{
391 $raw = self::readUnsignedVarInt($buffer, $offset);
392 $temp = ((($raw << 63) >> 63) ^ $raw) >> 1;
393 return $temp ^ ($raw & (1 << 63));
405 for($i = 0; $i <= 28; $i += 7){
406 if(!isset($buffer[$offset])){
409 $b = ord($buffer[$offset++]);
410 $value |= (($b & 0x7f) << $i);
412 if(($b & 0x80) === 0){
424 $v = ($v << 32 >> 32);
425 return self::writeUnsignedVarInt(($v << 1) ^ ($v >> 31));
435 $remaining = $value & 0xffffffff;
436 for($i = 0; $i < 5; ++$i){
437 if(($remaining >> 7) !== 0){
438 $buf .= chr($remaining | 0x80);
440 $buf .= chr($remaining & 0x7f);
444 $remaining = (($remaining >> 7) & (PHP_INT_MAX >> 6));
447 throw new InvalidArgumentException(
"Value too large to be encoded as a VarInt");
457 public static function readVarLong(
string $buffer,
int &$offset) : int{
458 $raw = self::readUnsignedVarLong($buffer, $offset);
459 $temp = ((($raw << 63) >> 63) ^ $raw) >> 1;
460 return $temp ^ ($raw & (1 << 63));
472 for($i = 0; $i <= 63; $i += 7){
473 if(!isset($buffer[$offset])){
476 $b = ord($buffer[$offset++]);
477 $value |= (($b & 0x7f) << $i);
479 if(($b & 0x80) === 0){
491 return self::writeUnsignedVarLong(($v << 1) ^ ($v >> 63));
500 for($i = 0; $i < 10; ++$i){
501 if(($remaining >> 7) !== 0){
502 $buf .= chr($remaining | 0x80);
504 $buf .= chr($remaining & 0x7f);
508 $remaining = (($remaining >> 7) & (PHP_INT_MAX >> 6));
511 throw new InvalidArgumentException(
"Value too large to be encoded as a VarLong");
static readRoundedLFloat(string $str, int $accuracy)
static writeShort(int $value)
static readShort(string $str)
static readDouble(string $str)
static readLLong(string $str)
static writeUnsignedVarInt(int $value)
static writeLDouble(float $value)
static writeUnsignedVarLong(int $value)
static readLong(string $str)
static writeDouble(float $value)
static readFloat(string $str)
static readUnsignedVarInt(string $buffer, int &$offset)
static writeTriad(int $value)
static readInt(string $str)
static readSignedShort(string $str)
static writeLLong(int $value)
static writeFloat(float $value)
static writeVarInt(int $v)
static writeLTriad(int $value)
static writeVarLong(int $v)
static writeLShort(int $value)
static writeLFloat(float $value)
static readLTriad(string $str)
static readUnsignedVarLong(string $buffer, int &$offset)
static writeLInt(int $value)
static writeBool(bool $b)
static readLInt(string $str)
static printFloat(float $value)
static readSignedByte(string $c)
static readRoundedFloat(string $str, int $accuracy)
static readByte(string $c)
static readVarLong(string $buffer, int &$offset)
static writeInt(int $value)
static readLDouble(string $str)
static readVarInt(string $buffer, int &$offset)
static writeLong(int $value)
static readLFloat(string $str)
static readSignedLShort(string $str)
static readLShort(string $str)
static readTriad(string $str)
static readBool(string $b)