Skip to content

Commit

Permalink
First look at ext-encoding integration
Browse files Browse the repository at this point in the history
in very brief tests, this yielded significant performance improvements, on the order of 2x for encoding and 1.5x for decoding.
  • Loading branch information
dktapps committed Oct 27, 2023
1 parent 2054027 commit 7bbadf8
Show file tree
Hide file tree
Showing 7 changed files with 101 additions and 100 deletions.
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"require": {
"php": "^7.4 || ^8.0",
"php-64bit": "*",
"pocketmine/binaryutils": "^0.2.0"
"ext-encoding": "~0.2.1"
},
"require-dev": {
"phpstan/phpstan": "1.10.25",
Expand Down
57 changes: 29 additions & 28 deletions src/BaseNbtSerializer.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,29 +23,27 @@

namespace pocketmine\nbt;

use pmmp\encoding\ByteBuffer;
use pmmp\encoding\DataDecodeException;
use pocketmine\nbt\tag\Tag;
use pocketmine\utils\Binary;
use pocketmine\utils\BinaryDataException;
use pocketmine\utils\BinaryStream;
use function strlen;

/**
* Base Named Binary Tag encoder/decoder
*/
abstract class BaseNbtSerializer implements NbtStreamReader, NbtStreamWriter{
/** @var BinaryStream */
protected $buffer;
protected ByteBuffer $buffer;

public function __construct(){
$this->buffer = new BinaryStream();
$this->buffer = new ByteBuffer();
}

/**
* @throws BinaryDataException
* @throws DataDecodeException
* @throws NbtDataException
*/
private function readRoot(int $maxDepth) : TreeRoot{
$type = $this->readByte();
$type = $this->buffer->readUnsignedByte();
if($type === NBT::TAG_End){
throw new NbtDataException("Found TAG_End at the start of buffer");
}
Expand All @@ -62,11 +60,12 @@ private function readRoot(int $maxDepth) : TreeRoot{
* @throws NbtDataException
*/
public function read(string $buffer, int &$offset = 0, int $maxDepth = 0) : TreeRoot{
$this->buffer = new BinaryStream($buffer, $offset);
$this->buffer = new ByteBuffer($buffer);
$this->buffer->setOffset($offset);

try{
$data = $this->readRoot($maxDepth);
}catch(BinaryDataException $e){
}catch(DataDecodeException $e){
throw new NbtDataException($e->getMessage(), 0, $e);
}
$offset = $this->buffer->getOffset();
Expand All @@ -85,7 +84,8 @@ public function read(string $buffer, int &$offset = 0, int $maxDepth = 0) : Tree
* @throws NbtDataException
*/
public function readHeadless(string $buffer, int $rootType, int &$offset = 0, int $maxDepth = 0) : Tag{
$this->buffer = new BinaryStream($buffer, $offset);
$this->buffer = new ByteBuffer($buffer);
$this->buffer->setOffset($offset);

$data = NBT::createTag($rootType, $this, new ReaderTracker($maxDepth));
$offset = $this->buffer->getOffset();
Expand All @@ -102,14 +102,15 @@ public function readHeadless(string $buffer, int $rootType, int &$offset = 0, in
* @throws NbtDataException
*/
public function readMultiple(string $buffer, int $maxDepth = 0) : array{
$this->buffer = new BinaryStream($buffer);
$this->buffer = new ByteBuffer($buffer);

$retval = [];

while(!$this->buffer->feof()){
$strlen = strlen($buffer);
while($this->buffer->getOffset() < $strlen){
try{
$retval[] = $this->readRoot($maxDepth);
}catch(BinaryDataException $e){
}catch(DataDecodeException $e){
throw new NbtDataException($e->getMessage(), 0, $e);
}
}
Expand All @@ -118,17 +119,17 @@ public function readMultiple(string $buffer, int $maxDepth = 0) : array{
}

private function writeRoot(TreeRoot $root) : void{
$this->writeByte($root->getTag()->getType());
$this->buffer->writeUnsignedByte($root->getTag()->getType());
$this->writeString($root->getName());
$root->getTag()->write($this);
}

public function write(TreeRoot $data) : string{
$this->buffer = new BinaryStream();
$this->buffer = new ByteBuffer();

$this->writeRoot($data);

return $this->buffer->getBuffer();
return $this->buffer->toString();
}

/**
Expand All @@ -138,45 +139,45 @@ public function write(TreeRoot $data) : string{
* @see BaseNbtSerializer::readHeadless()
*/
public function writeHeadless(Tag $data) : string{
$this->buffer = new BinaryStream();
$this->buffer = new ByteBuffer();
$data->write($this);
return $this->buffer->getBuffer();
return $this->buffer->toString();
}

/**
* @param TreeRoot[] $data
*/
public function writeMultiple(array $data) : string{
$this->buffer = new BinaryStream();
$this->buffer = new ByteBuffer();
foreach($data as $root){
$this->writeRoot($root);
}
return $this->buffer->getBuffer();
return $this->buffer->toString();
}

public function readByte() : int{
return $this->buffer->getByte();
return $this->buffer->readUnsignedByte();
}

public function readSignedByte() : int{
return Binary::signByte($this->buffer->getByte());
return $this->buffer->readSignedByte();
}

public function writeByte(int $v) : void{
$this->buffer->putByte($v);
$this->buffer->writeUnsignedByte($v);
}

public function readByteArray() : string{
$length = $this->readInt();
if($length < 0){
throw new NbtDataException("Array length cannot be less than zero ($length < 0)");
}
return $this->buffer->get($length);
return $this->buffer->readByteArray($length);
}

public function writeByteArray(string $v) : void{
$this->writeInt(strlen($v)); //TODO: overflow
$this->buffer->put($v);
$this->buffer->writeByteArray($v);
}

/**
Expand All @@ -200,14 +201,14 @@ protected static function checkWriteStringLength(int $len) : int{
}

public function readString() : string{
return $this->buffer->get(self::checkReadStringLength($this->readShort()));
return $this->buffer->readByteArray(self::checkReadStringLength($this->readShort()));
}

/**
* @throws \InvalidArgumentException if the string is too long
*/
public function writeString(string $v) : void{
$this->writeShort(self::checkWriteStringLength(strlen($v)));
$this->buffer->put($v);
$this->buffer->writeByteArray($v);
}
}
26 changes: 13 additions & 13 deletions src/BigEndianNbtSerializer.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,61 +32,61 @@
class BigEndianNbtSerializer extends BaseNbtSerializer{

public function readShort() : int{
return $this->buffer->getShort();
return $this->buffer->readUnsignedShortBE();
}

public function readSignedShort() : int{
return $this->buffer->getSignedShort();
return $this->buffer->readSignedShortBE();
}

public function writeShort(int $v) : void{
$this->buffer->putShort($v);
$this->buffer->writeUnsignedShortBE($v);
}

public function readInt() : int{
return $this->buffer->getInt();
return $this->buffer->readSignedIntBE();
}

public function writeInt(int $v) : void{
$this->buffer->putInt($v);
$this->buffer->writeSignedIntBE($v);
}

public function readLong() : int{
return $this->buffer->getLong();
return $this->buffer->readSignedLongBE();
}

public function writeLong(int $v) : void{
$this->buffer->putLong($v);
$this->buffer->writeSignedLongBE($v);
}

public function readFloat() : float{
return $this->buffer->getFloat();
return $this->buffer->readFloatBE();
}

public function writeFloat(float $v) : void{
$this->buffer->putFloat($v);
$this->buffer->writeFloatBE($v);
}

public function readDouble() : float{
return $this->buffer->getDouble();
return $this->buffer->readDoubleBE();
}

public function writeDouble(float $v) : void{
$this->buffer->putDouble($v);
$this->buffer->writeDoubleBE($v);
}

public function readIntArray() : array{
$len = $this->readInt();
if($len < 0){
throw new NbtDataException("Array length cannot be less than zero ($len < 0)");
}
$unpacked = unpack("N*", $this->buffer->get($len * 4));
$unpacked = unpack("N*", $this->buffer->readByteArray($len * 4));
assert($unpacked !== false, "The formatting string is valid, and we gave a multiple of 4 bytes");
return array_values($unpacked);
}

public function writeIntArray(array $array) : void{
$this->writeInt(count($array));
$this->buffer->put(pack("N*", ...$array));
$this->buffer->writeByteArray(pack("N*", ...$array));
}
}
Loading

0 comments on commit 7bbadf8

Please sign in to comment.