diff --git a/.travis.yml b/.travis.yml index ba81726..93d34bc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,32 +1,26 @@ language: php php: + - nightly + - 8.0 - 7.3 - 7.2 - - 7.1 - - 7.0 - - 5.6 - - 5.5 - - 5.4 - - hhvm - + +env: + matrix: + - PREFER_LOWEST="--prefer-lowest" + - PREFER_LOWEST="" + matrix: allow_failures: - - php: 5.5 - - php: 5.4 - - php: hhvm - + - php: nightly + before_install: - composer self-update after_script: - wget https://scrutinizer-ci.com/ocular.phar - php ocular.phar code-coverage:upload --format=php-clover coverage.xml - + install: - - | - if [[ "$(phpenv version-name)" < "5.6" ]] - then - travis_retry composer require --no-interaction --prefer-source 'phpunit/phpunit:^4.8' - fi - - travis_retry composer update --no-interaction --prefer-source \ No newline at end of file + - travis_retry composer update --no-interaction --prefer-source $PREFER_LOWEST diff --git a/README.md b/README.md index afe72bd..8c0ba20 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,13 @@ Binn ==== -[![Build Status](https://travis-ci.org/et-nik/binn-php.svg?branch=master)](https://travis-ci.org/et-nik/binn-php) +[![Build Status](https://travis-ci.com/et-nik/binn-php.svg?branch=master)](https://travis-ci.comd/et-nik/binn-php) [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/et-nik/binn-php/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/et-nik/binn-php/?branch=master) [![Code Coverage](https://scrutinizer-ci.com/g/et-nik/binn-php/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/et-nik/binn-php/?branch=master) PHP Class for serialize to binary string. -Original Binn Library for C++ - https://github.com/liteserver/binn +Original C Binn Library: https://github.com/liteserver/binn Binn Specification: https://github.com/liteserver/binn/blob/master/spec.md @@ -26,7 +26,7 @@ Sequential arrays: ```php use Knik\Binn\Binn; -$binn = new Binn; +$binn = new Binn(); // List $array = [123, -456, 789]; @@ -36,7 +36,7 @@ $unserialized = $binn->unserialize($binnString); // Equal with $array Numeric keys array: ```php -$binn = new Binn; +$binn = new Binn(); // Map $array = [1 => "add", 2 => [-12345, 6789]]; @@ -46,7 +46,7 @@ $unserialized = $binn->unserialize($binnString); // Equal with $array String keys array: ```php -$binn = new Binn; +$binn = new Binn(); // Object $array = ["hello" => "world"]; @@ -57,7 +57,7 @@ $unserialized = $binn->unserialize($binnString); // Equal with $array Mixed arrays: ```php -$binn = new Binn; +$binn = new Binn(); $array = [ ["id" => 1, "name" => "John"], ["id" => 2, "name" => "Eric"] ] // A list of objects @@ -65,43 +65,33 @@ $binnString = $binn->serialize($array); $unserialized = $binn->unserialize($binnString); // Equal with $array ``` -### Binn List - -Serialize/unserialize sequential arrays - -### Simple example - +Blob: ```php -use Knik\Binn\BinnList; +$binn = new Binn(); +$file = fopen('/path/to/file.jpg', 'rb'); -$array = [4, -8875, 'text']; +// Filedata in binn structure +$bin1 = $binn->serialize($file); -$binn = new BinnList(); +// Filedata in binn list structure +$bin2 = $binn->serialize(['file' => $file]); +``` -// \xE0\x0F\x03\x20\x04\x41\xDD\x55\xA0\x04text\x00 -$serialized = $binn->serialize($array); +### Symfony Serializer -``` +You can use BinnEncoder with Symfony Serializer ```php -$binnString = "\xE0\x0F\x03\x20\x04\x41\xDD\x55\xA0\x04text\x00"; +use Knik\Binn\Encoder\BinnEncoder; +use Symfony\Component\Serializer\Serializer; -$binn = new BinnList(); -$unserialized = $binn->unserialize($binnString); - -/* -Array -( - [0] => 4 - [1] => -8875 - [2] => text -) -*/ -print_r($unserialized); +$encoders = [new BinnEncoder()]; +$serializer = new Serializer([], $encoders); +$serializer->serialize("\x40\xD0\x06", 'binn'); ``` -### Original C++ library style +### Original C library style ```php $binn = new BinnList(); $binn->addUint8(4); @@ -122,4 +112,4 @@ $binn = new BinnList(); // \xE0\x11\x03\x20\x02\x01\xE0\x0B\x03\x20\x7B\x41\xFE\x38\x40\x03\x15 $serialized = $binn->serialize($array); -``` \ No newline at end of file +``` diff --git a/composer.json b/composer.json index 2ea2b47..9a0fc63 100644 --- a/composer.json +++ b/composer.json @@ -13,15 +13,16 @@ } ], "require": { - "php": ">=5.6.3" + "php": ">=7.2", + "symfony/serializer": "^5" }, "require-dev": { - "mockery/mockery": "0.9.*", - "phpunit/phpunit": "^5.7" + "phpunit/phpunit": "^8.0 || ^9.0" }, "autoload": { "psr-4": { - "Knik\\Binn\\": "src/" + "Knik\\Binn\\": "src/", + "Knik\\Binn\\Tests\\": "tests/" } } } diff --git a/coverage.xml b/coverage.xml deleted file mode 100644 index ae643cb..0000000 --- a/coverage.xml +++ /dev/null @@ -1,719 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/examples/binn_example.php b/examples/binn_example.php index a22716e..e2ad854 100644 --- a/examples/binn_example.php +++ b/examples/binn_example.php @@ -16,9 +16,9 @@ // Read $binnString = file_get_contents("test.bin"); -$readBinn = new BinnList($binnString); +$readBinn = new BinnList(); -print_r($readBinn->unserialize()) . PHP_EOL; +print_r($readBinn->unserialize($binnString)) . PHP_EOL; $array = [2, true, [123, -456, 789]]; @@ -27,9 +27,9 @@ // $binnString = $serialized; // -for ($i = 0; $i < strlen($binnString); $i++) { +for ($i = 0, $iMax = strlen($binnString); $i < $iMax; $i++) { echo "\\x" . strtoupper(str_pad(dechex(ord($binnString[$i])), 2, '0', STR_PAD_LEFT)); } -echo PHP_EOL; \ No newline at end of file +echo PHP_EOL; diff --git a/phpunit.xml b/phpunit.xml index 371f231..dbb74c8 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -1,36 +1,8 @@ - + - - tests + + ./tests/Unit - - - - ./src/ - - - ./examples/ - - - - - - - - - - - - \ No newline at end of file + diff --git a/src/Binn.php b/src/Binn.php index 5022528..7ef756d 100644 --- a/src/Binn.php +++ b/src/Binn.php @@ -3,8 +3,6 @@ * Binn. Serialize to bin string. * Binn Specification: https://github.com/liteserver/binn/blob/master/spec.md * - * Note! This class not support Map and Object, only List support. Sorry, i am working on this. - * * Original Binn Library for C++ - https://github.com/liteserver/binn * * @@ -17,72 +15,85 @@ namespace Knik\Binn; -class Binn extends BinnAbstract { - +class Binn extends BinnAbstract +{ /** - * Size bin string in bytes - * - * @var int - * @access protected + * State. Will be removed in 1.0 + * @var array */ - protected $size = 0; + protected $items = []; /** - * Bin string - * + * State. Will be removed in 1.0 * @var string - * @access protected */ - protected $binnString = ""; + protected $binn = ''; + + public function serialize($data = null) + { + if ($data === null) { + return $this->encoder->encode($this->items, 'binn'); + } + + return $this->encoder->encode($data, 'binn'); + } + + public function unserialize($binnString = null) + { + if ($binnString === null) { + return $this->decoder->decode($this->binn, 'binn'); + } + + return $this->decoder->decode($binnString, 'binn'); + } /** - * Binn constructor + * @deprecated use serialize/unserialize */ - public function __construct() + public function binnOpen(string $binn = ''): void { - $this->setContainersClasses([ - self::BINN_LIST => BinnList::class, - self::BINN_MAP => BinnMap::class, - self::BINN_OBJECT => BinnObject::class, - ]); + $this->binn = $binn; + $this->items = $this->unserialize($binn); } /** - * @param array $array - * @return string + * @deprecated use serialize/unserialize */ - public function serialize($array = []) + public function getBinnVal(): string { - $this->binnFree(); + $this->binn = $this->serialize(); + return $this->binn; + } - foreach ($this->containersClasses as $contanerType => $containersClass) - { - if ($containersClass::validArray($array)) { - $container = new $containersClass(); - return $container->serialize($array); - } - } + /** + * @deprecated + */ + public function getBinnArr(): array + { + return $this->items; } /** - * @param string $binnString - * @return array|null + * @deprecated */ - public function unserialize($binnString = '') + public function binnSize(): int { - if (empty($binnString)) { - return $this->getBinnArr(); - } + return strlen($this->binn); + } - $this->binnFree(); + /** + * @deprecated + */ + public function binnFree() + { + $this->binn = ''; + $this->items = []; - $type = $this->unpack(Binn::BINN_UINT8, $binnString[0]); + return $this; + } - if (array_key_exists($type, $this->containersClasses)) { - $binnContainer = new $this->containersClasses[$type]($binnString); - return $binnContainer->unserialize(); - } else { - return null; - } + public function toArray(): array + { + return $this->items; } -} \ No newline at end of file +} diff --git a/src/BinnAbstract.php b/src/BinnAbstract.php index 607bd0a..7703ae3 100644 --- a/src/BinnAbstract.php +++ b/src/BinnAbstract.php @@ -2,6 +2,11 @@ namespace Knik\Binn; +use Knik\Binn\Decoder\BinnDecode; +use Knik\Binn\Decoder\DecoderCollectionFactory; +use Knik\Binn\Encoder\BinnEncode; +use Knik\Binn\Encoder\EncoderCollectionFactory; + abstract class BinnAbstract { // Consts from original C++ Library @@ -55,531 +60,44 @@ abstract class BinnAbstract const BINN_STORAGE_MASK = 0xE0; const BINN_TYPE_MASK = 0x0F; + const BINN_STORAGE_HAS_MORE = 0x10; + + const BINN_JPEG = 0xD001; + const BINN_GIF = 0xD002; + const BINN_PNG = 0xD003; + const BINN_BMP = 0xD004; + const MIN_BINN_SIZE = 3; - // PHP Library consts - const KEY_TYPE = 0; - const KEY_VAL = 1; - const KEY_SIZE = 2; - const KEY_KEY = 3; + const BINN_MAX_ONE_BYTE_SIZE = 127; /** * Binn object type: self::BINN_LIST, self::BINN_MAP, self::BINN_OBJECT - * - * @var int $binnType - * @access protected */ protected $binnType = self::BINN_NULL; - /** - * @var string - */ - protected $binnClass = null; - - /** - * Count elements in object - * - * @var int - * @access protected - */ - protected $count = 0; - - /** - * Data size in bytes - * - * @var int - * @access protected - */ - protected $dataSize = 0; - - /** - * Meta size in bytes - * - * @var int - */ - protected $metaSize = self::MIN_BINN_SIZE; - - /** - * Size bin string in bytes - * - * @var int - * @access protected - */ - protected $size = 0; - - /** - * Bin string - * - * @var string - * @access protected - */ - protected $binnString = ""; + /** @var BinnEncode */ + protected $encoder; - /** - * Object elements - * - * @var array - * @access protected - */ - protected $binnArr = []; + /** @var BinnDecode */ + protected $decoder; - /** - * @var array - * - * Associations container int with container classes - * - * Example values: - * [ - * 0xE0 => \Knik\Binn\BinnList::class, - * 0xE1 => \Knik\Binn\BinnMap::class, - * 0xE2 => \Knik\Binn\BinnObject::class, - * ] - */ - protected $containersClasses = [ - self::BINN_LIST => \Knik\Binn\BinnList::class, - self::BINN_MAP => \Knik\Binn\BinnMap::class, - self::BINN_OBJECT => \Knik\Binn\BinnObject::class, - ]; - - /** - * @param $containersClasses - */ - public function setContainersClasses($containersClasses) - { - $this->containersClasses = $containersClasses; - } - - /** - * Get 4 bytes packed size. Add cut bit. - * - * @param int $intVal - * @return string - */ - protected function getInt32Binsize($intVal = 0) - { - $intVal = ($intVal | (1 << 31)); // Add bit - return $this->pack(self::BINN_UINT32, $intVal); - } - - /** - * Detect value type - * - * @param mixed $value - * @return int - */ - protected function detectType($value) - { - if (is_bool($value)) { - return $value ? self::BINN_TRUE : self::BINN_FALSE; - } - - if (is_string($value)) { - return self::BINN_STRING; - } - - if (is_integer($value)) { - return $this->detectInt($value); - } - - if (is_float($value)) { - if (strlen($value) > 4) { - return self::BINN_FLOAT64; - } else { - return self::BINN_FLOAT32; - } - } - - if (is_array($value)) { - foreach ($this->containersClasses as $contanerType => $containersClass) { - if ($containersClass::validArray($value)) { - return $contanerType; - } - } - } - - return self::BINN_NULL; - } - - /** - * Detect integer type - * - * @param $value - * @return int - */ - protected function detectInt($value) - { - if ($value < 0) { - // int - if ($value >= self::INT8_MIN) { - return self::BINN_INT8; - } else if ($value >= self::INT16_MIN) { - return self::BINN_INT16; - } else if ($value >= self::INT32_MIN) { - return self::BINN_INT32; - } else { - return self::BINN_INT64; - } + public function __construct( + ?BinnEncode $encoder = null, + ?BinnDecode $decoder = null + ) { + if ($encoder === null) { + $factory = new EncoderCollectionFactory(); + $this->encoder = new BinnEncode($factory->getCollection()); } else { - // uint - if ($value <= self::UINT8_MAX) { - return self::BINN_UINT8; - } else if ($value <= self::UINT16_MAX) { - return self::BINN_UINT16; - } else if ($value <= self::UINT32_MAX) { - return self::BINN_UINT32; - } else { - return self::BINN_UINT64; - } - } - } - - /** - * Get storage type - * - * @param $type - * @return int - */ - protected function storageType($type) - { - return $type & ($type ^ self::BINN_TYPE_MASK); - } - - /** - * Array associativity check - * True if array is associative, False if array is sequential - * - * @param array $arr - * @return bool - */ - protected static function isArrayAssoc($arr) - { - $arr = (array)$arr; - if (array() === $arr) return false; - return array_keys($arr) !== range(0, count($arr) - 1); - } - - /** - * Array objectivity check - * True if array is objective, False if array is sequential or have only number keys - * - * @param $arr - * @return bool - */ - protected static function isArrayObject($arr) - { - foreach(array_keys($arr) as $key) { - if (!is_int($key)) { - return true; - } - } - - return false; - } - - /** - * Calculate result binary Binn string size - * @return int - */ - protected function calculateSize() - { - $size = 0; - - if (($this->dataSize + $this->metaSize) > 127) { - $size += 3; - } - - if (count($this->binnArr) > 127) { - $size += 3; - } - - $this->size = ($this->dataSize + $this->metaSize) + $size; - return $this->size; - } - - /** - * - * @return array - */ - public function getBinnArr() - { - $return = []; - - foreach ($this->binnArr as $arr) { - $storageType = $this->storageType($arr[self::KEY_TYPE]); - - if ($storageType === self::BINN_STORAGE_CONTAINER) { - if (isset($arr[self::KEY_KEY])) { - $key = $arr[self::KEY_KEY]; - $return[$key] = $arr[self::KEY_VAL]->getBinnArr(); - } else { - $return[] = $arr[self::KEY_VAL]->getBinnArr(); - } - } else { - if (isset($arr[self::KEY_KEY])) { - $key = $arr[self::KEY_KEY]; - $return[$key] = $arr[self::KEY_VAL]; - } else { - $return[] = $arr[self::KEY_VAL]; - } - } - } - - return $return; - } - - /** - * Get binn size - * @return int - */ - public function binnSize() - { - return $this->calculateSize(); - } - - /** - * Memory saving - * If it possible: - * Converting int64 to int32/int16/int8 - * Converting uint64 to uint32/uint16/uint8 - * Converting positive int to uint - * - * @param int $type - * @param mixed $val - * - * @return int $type2 - * - */ - protected function compressInt($type, $val) - { - $newType = $type; - - if ($val >= 0) { - // Convert to unsigned - switch ($newType) { - case self::BINN_INT64: - $newType = self::BINN_UINT64; - break; - - case self::BINN_INT32: - $newType = self::BINN_UINT32; - break; - - case self::BINN_INT16: - $newType = self::BINN_UINT16; - break; - - case self::BINN_INT8: - $newType = self::BINN_UINT8; - break; - } + $this->encoder = $encoder; } - if (in_array($newType, [self::BINN_INT64, self::BINN_INT32, self::BINN_INT16])) { - // Signed - if ($val >= self::INT8_MIN) { - $newType = self::BINN_INT8; - } - elseif ($val >= self::INT16_MIN) { - $newType = self::BINN_INT16; - } - elseif ($val >= self::INT32_MIN) { - $newType = self::BINN_INT32; - } - } - - if (in_array($newType, [self::BINN_UINT64, self::BINN_UINT32, self::BINN_UINT16])) { - // Unsigned - - if ($val <= self::UINT8_MAX) { - $newType = self::BINN_UINT8; - } - elseif ($val <= self::UINT16_MAX) { - $newType = self::BINN_UINT16; - } - elseif ($val <= self::UINT32_MAX) { - $newType = self::BINN_UINT32; - } - } - - return $newType; - } - - /** - * Clear all binn data - * - * @return $this - */ - public function binnFree() - { - // $this->binnType = self::BINN_STORAGE_NOBYTES; - - $this->count = 0; - $this->dataSize = 0; - - // Initial meta size 3 bytes - // Type byte + Size byte + Item counts byte - $this->metaSize = self::MIN_BINN_SIZE; - - $this->size = 0; - $this->binnString = ""; - - $this->binnArr = []; - - return $this; - } - - /** - * Unpack value - * - * @param $varType - * @param $value - * @return bool|null - */ - protected function unpack($varType, $value) - { - if ($varType === self::BINN_TRUE) { - return true; - } else if ($varType === self::BINN_FALSE) { - return false; - } else if ($varType === self::BINN_UINT64) { - return unpack("J", $value)[1]; - } else if ($varType === self::BINN_UINT32) { - return unpack("N", $value)[1]; - } else if ($varType === self::BINN_UINT16) { - return unpack("n", $value)[1]; - } else if ($varType == self::BINN_UINT8) { - return unpack("C", $value)[1]; - } else if ($varType === self::BINN_INT8) { - return unpack("c", $value)[1]; - } else if ($varType === self::BINN_INT16) { - return unpack("s", strrev($value))[1]; - } else if ($varType === self::BINN_INT32) { - return unpack("i", strrev($value))[1]; - } else if ($varType === self::BINN_INT64) { - return unpack("q", strrev($value))[1]; - } else if ($varType === self::BINN_FLOAT32) { - return unpack("f", strrev($value))[1]; - } else if ($varType === self::BINN_FLOAT64) { - return unpack("d", strrev($value))[1]; - } else if ($varType === self::BINN_STRING) { - return unpack("a*", $value)[1]; - } - - return null; - } - - /** - * Pack value - * - * @param $varType - * @param mixed $value - * @return null|string - */ - protected function pack($varType, $value = null) - { - if ($varType === self::BINN_TRUE) { - return pack("C", self::BINN_TRUE); - } else if ($varType === self::BINN_FALSE) { - return pack("C", self::BINN_FALSE); - } else if ($varType === self::BINN_UINT64) { - return pack("J", $value); - } else if ($varType === self::BINN_UINT32) { - return pack("N", $value); - } else if ($varType === self::BINN_UINT16) { - return pack("n", $value); - } else if ($varType === self::BINN_UINT8) { - return pack("C", $value); - } else if ($varType === self::BINN_INT8) { - return pack("c", $value); - } else if ($varType === self::BINN_INT16) { - return strrev(pack("s", $value)); - } else if ($varType === self::BINN_INT32) { - return strrev(pack("i", $value)); - } else if ($varType === self::BINN_INT64) { - return strrev(pack("q", $value)); - } else if ($varType === self::BINN_FLOAT32) { - return strrev(pack("f", $value)); - } else if ($varType === self::BINN_FLOAT64) { - return strrev(pack("d", $value)); - } else if ($varType === self::BINN_STRING) { - return pack("a*", $value); - } else if ($varType === self::BINN_NULL) { - return pack("x"); - } - - return null; - } - - /** - * Pack varType - * - * @param $type - * @return string - */ - protected function packType($type) - { - return $this->pack(self::BINN_UINT8, $type); - } - - /** - * Pack size info - * - * @param $size - * @return string - */ - protected function packSize($size) - { - return ($size <= 127) - ? $this->pack(self::BINN_UINT8, $size) - : $this->getInt32Binsize($size); - } - - /** - * Get size info - * data and meta (type info, size info, null bytes) - * - * @param $type - * @param string $value - * @return array - */ - protected function getTypeSize($type, $value = '') - { - $size = ['meta' => 0, 'data' => 0]; - $storageType = $this->storageType($type); - - if ($type == self::BINN_BOOL - || $type == self::BINN_TRUE - || $type == self::BINN_FALSE - ) { - $size = ['meta' => 1, 'data' => 0]; - } else if ($storageType === self::BINN_STORAGE_CONTAINER) { - $size = ['meta' => 0, 'data' => $value->binnSize()]; - } else if ($storageType === self::BINN_STORAGE_BLOB) { - $dataSize = mb_strlen($value); - - $metaSize = $dataSize > 127 ? 4 : 1; // size byte - $metaSize += 1; // type byte - - $size = ['meta' => $metaSize, 'data' => $dataSize]; - } else if ($storageType === self::BINN_STORAGE_STRING) { - $dataSize = mb_strlen($value); - - $metaSize = $dataSize > 127 ? 4 : 1; // size byte - $metaSize += 2; // type byte + null terminated - - $size = ['meta' => $metaSize, 'data' => $dataSize]; - } else if ($storageType === self::BINN_STORAGE_QWORD) { - $size = ['meta' => 1, 'data' => 8]; - } else if ($storageType === self::BINN_STORAGE_DWORD) { - $size = ['meta' => 1, 'data' => 4]; - } else if ($storageType === self::BINN_STORAGE_WORD) { - $size = ['meta' => 1, 'data' => 2]; - } else if ($storageType === self::BINN_STORAGE_BYTE) { - $size = ['meta' => 1, 'data' => 1]; - } else if ($storageType === self::BINN_STORAGE_NOBYTES) { - $size = ['meta' => 1, 'data' => 0]; + if ($decoder === null) { + $factory = new DecoderCollectionFactory(); + $this->decoder = $decoder ?? new BinnDecode($factory->getCollection()); + } else { + $this->decoder = $decoder; } - - return $size; } -} \ No newline at end of file +} diff --git a/src/BinnList.php b/src/BinnList.php index 3b4e62f..54067e2 100644 --- a/src/BinnList.php +++ b/src/BinnList.php @@ -2,7 +2,7 @@ namespace Knik\Binn; -use Knik\Binn\Exceptions\InvalidArrayException; +use Knik\Binn\Contracts\Container; /** * @method BinnList addBool(boolean $value) @@ -17,18 +17,14 @@ * @method BinnList addFloat(string $value) * @method BinnList addDouble(string $value) * @method BinnList addStr(string $value) - * @method BinnList addList(Binn $value) * @method BinnList addMap(Binn $value) * @method BinnList addObject(Binn $value) * */ -class BinnList extends BinnAbstract +class BinnList extends Binn implements Container { protected $binnType = self::BINN_LIST; - /** - * @var array - */ private $methodsAssignments = [ 'addBool' => self::BINN_BOOL, 'addUint8' => self::BINN_UINT8, @@ -47,286 +43,23 @@ class BinnList extends BinnAbstract 'addObject' => self::BINN_OBJECT, ]; - public function __construct($binnString = '') - { - $this->binnClass = self::class; - - if ($binnString != '') { - $this->_binnLoad($binnString); - } - - return $this; - } - - /** - * @param $name - * @param $arguments - * @return $this - * - * @throws \Exception - */ public function __call($name, $arguments) { if (array_key_exists($name, $this->methodsAssignments)) { - $this->_addVal($this->methodsAssignments[$name], $arguments[0]); + $this->addVal($arguments[0]); return $this; } throw new \Exception("Call to undefined method {$name}"); } - /** - * @param string $binnString - */ - public function binnOpen($binnString = '') - { - if ($binnString != '') { - $this->_binnLoad($binnString); - } - } - - /** - * Get binary string - * - * @return string - */ - public function getBinnVal() - { - $this->calculateSize(); - - $this->binnString = ''; - $this->binnString .= $this->pack(self::BINN_UINT8, $this->binnType); - - $this->binnString .= $this->packSize($this->size); - - $count = count($this->binnArr); - $this->binnString .= $this->packSize($count); - - foreach ($this->binnArr as &$arr) { - $type = $arr[self::KEY_TYPE]; - $storageType = $this->storageType($type); - - if ($type === self::BINN_BOOL) { - $this->binnString .= $arr[self::KEY_VAL] - ? $this->packType(self::BINN_TRUE) - : $this->packType(self::BINN_FALSE); - - continue; - } - - if ($storageType === self::BINN_STORAGE_QWORD - || $storageType === self::BINN_STORAGE_DWORD - || $storageType === self::BINN_STORAGE_WORD - || $storageType === self::BINN_STORAGE_BYTE - ) { - $this->binnString .= $this->packType($arr[self::KEY_TYPE]); - $this->binnString .= $this->pack($arr[self::KEY_TYPE], $arr[self::KEY_VAL]); - } else if ($storageType === self::BINN_STORAGE_NOBYTES) { - $this->binnString .= $this->packType($arr[self::KEY_TYPE]); - } else if ($storageType === self::BINN_STORAGE_STRING) { - $this->binnString .= $this->packType(self::BINN_STRING); - $this->binnString .= $this->packSize($arr[self::KEY_SIZE]); - $this->binnString .= $this->pack(self::BINN_STRING, $arr[self::KEY_VAL]); - $this->binnString .= $this->pack(self::BINN_NULL); - } else if ($storageType === self::BINN_STORAGE_CONTAINER) { - $this->binnString .= $arr[self::KEY_VAL]->getBinnVal(); - } - } - - return $this->binnString; - } - - /** - * Check is valid array to serialize - * - * @param $array - * @return bool - */ - public static function validArray($array) - { - $array = (array)$array; - if (self::isArrayAssoc($array)) { - return false; - } - - return true; - } - - /** - * @param array $array - * @return string - */ - public function serialize($array = []) - { - if (empty($array)) { - return $this->getBinnVal(); - } - - $this->binnFree(); - - if ($this->isArrayAssoc($array)) { - throw new InvalidArrayException('Array should be sequential'); - } - - foreach ($array as $item) { - $type = $this->detectType($item); - $storageType = $this->storageType($type); - - if ($storageType === self::BINN_STORAGE_CONTAINER) { - foreach ($this->containersClasses as $contanerType => $containersClass) - { - if ($containersClass::validArray($item)) { - $container = new $containersClass(); - $container->serialize($item); - $item = $container; - break; - } - } - } - - $this->_addVal($type, $item); - } - - return $this->getBinnVal(); - } - - /** - * @param string $binnString - * @return array - */ - public function unserialize($binnString = '') + public function addList(BinnList $list): void { - if (empty($binnString)) { - return $this->getBinnArr(); - } - - $this->binnFree(); - - $this->binnOpen($binnString); - return $this->getBinnArr(); + $this->addVal($list->toArray()); } - /** - * @param int $type - * @param mixed $value - */ - private function _addVal($type, $value) + private function addVal($value): void { - if (in_array($type, - [self::BINN_INT64, self::BINN_INT32, self::BINN_INT16, - self::BINN_UINT64,self::BINN_UINT32, self::BINN_UINT16]) - ) { - $type = $this->compressInt($type, $value); - } - - $size = $this->getTypeSize($type, $value); - - $this->dataSize += $size['data']; - $this->metaSize += $size['meta']; - - $this->count++; - - $this->binnArr[] = [ - self::KEY_TYPE => $type, - self::KEY_VAL => $value, - self::KEY_SIZE => $size['data'] - ]; - } - - /** - * @param string - */ - private function _binnLoad($binnString) - { - $pos = 1; // Position - $sizeBytes = $this->unpack(self::BINN_UINT8, $binnString[$pos]); - - // Size - if ($sizeBytes & 1 << 7) { - $sizeBytes = $this->unpack(self::BINN_UINT32, substr($binnString, $pos, 4)); - $this->size = ($sizeBytes &~ (1 << 31)); // Cut bit - $pos += 4; - } else { - $this->size = $sizeBytes; - $pos += 1; - } - - unset($sizeBytes); - - $countBytes = $this->unpack(self::BINN_UINT8, $binnString[$pos]); - - // Size - if ($countBytes & 1 << 7) { - $countBytes = $this->unpack(self::BINN_UINT32, substr($binnString, $pos, 4)); - $this->count = ($countBytes &~ (1 << 31)); // Cut bit - $pos += 4; - } else { - $this->count = $countBytes; - $pos += 1; - } - - unset($countBytes); - - // Data - $stop_while = false; - while ($pos < $this->size && !$stop_while) { - $varType = $this->unpack(self::BINN_UINT8, $binnString[$pos]); - $varStorageType = $this->storageType($varType); - $pos += 1; - - if ($varStorageType === self::BINN_STORAGE_QWORD - || $varStorageType === self::BINN_STORAGE_DWORD - || $varStorageType === self::BINN_STORAGE_WORD - || $varStorageType === self::BINN_STORAGE_BYTE - || $varStorageType === self::BINN_STORAGE_NOBYTES - ) { - $varSize = $this->getTypeSize($varType); - $val = $this->unpack($varType, substr($binnString, $pos, $varSize['data'])); - $this->_addVal($varType, $val); - $pos += $varSize['data']; - - } else if ($varStorageType === self::BINN_STRING ) { - $stringSize = $this->unpack(self::BINN_UINT8, $binnString[$pos]); - - // Size - if ($stringSize & 1 << 7) { - $stringSize = $this->unpack(self::BINN_UINT32, substr($binnString, $pos, 4)); - $stringSize = ($stringSize &~ (1 << 31)); // Cut bit - $pos += 4; - } else { - $pos += 1; - } - - $this->_addVal(self::BINN_STRING, $this->unpack( - self::BINN_STRING, - substr($binnString, $pos, $stringSize) - )); - - $pos += $stringSize; - $pos += 1; // Null byte - } else if ($varStorageType === self::BINN_STORAGE_CONTAINER) { - $list_size = $this->unpack(self::BINN_UINT8, $binnString[$pos]);; - - // Size - if ($list_size & 1 << 7) { - $list_size = $this->unpack(self::BINN_UINT32, substr($binnString, $pos, 4)); - $list_size = ($list_size &~ (1 << 31)); // Cut bit - } - - $substring = substr($binnString, $pos-1, $list_size); - - foreach ($this->containersClasses as $containerType => $containersClass) { - if ($containerType === $varType) { - $container = new $containersClass($substring); - $this->_addVal($varType, $container); - break; - } - } - - $pos += ($list_size-1); - } else { - $stop_while = true; - } - } + $this->items[] = $value; } -} \ No newline at end of file +} diff --git a/src/BinnMap.php b/src/BinnMap.php index a3704d8..d71492a 100644 --- a/src/BinnMap.php +++ b/src/BinnMap.php @@ -2,291 +2,14 @@ namespace Knik\Binn; -use Knik\Binn\Exceptions\InvalidArrayException; +use Knik\Binn\Contracts\Container; -class BinnMap extends BinnAbstract +class BinnMap extends Binn implements Container { protected $binnType = self::BINN_MAP; - public function __construct($binnString = '') + private function addVal($key, $value) { - $this->binnType = self::BINN_MAP; - $this->binnClass = self::class; - - if ($binnString != '') { - $this->_binnLoad($binnString); - } - - return $this; - } - - /** - * @param string $binnString - */ - public function binnOpen($binnString = '') - { - if ($binnString != '') { - $this->_binnLoad($binnString); - } - } - - /** - * @param integer $key - * @param int $type - * @param mixed $value - */ - private function _addVal($key, $type, $value) - { - if (in_array($type, - [self::BINN_INT64, self::BINN_INT32, self::BINN_INT16, - self::BINN_UINT64,self::BINN_UINT32, self::BINN_UINT16]) - ) { - $type = $this->compressInt($type, $value); - } - - $size = $this->getTypeSize($type, $value); - - $this->dataSize += $size['data']; - $this->metaSize += $size['meta']; - - // Key size. 4 bytes - $this->metaSize += 4; - - $this->count++; - - $this->binnArr[] = [ - self::KEY_TYPE => $type, - self::KEY_VAL => $value, - self::KEY_SIZE => $size['data'], - self::KEY_KEY => $key, - ]; - } - - /** - * @param string - */ - private function _binnLoad($binnString) - { - $pos = 1; // Position - $sizeBytes = $this->unpack(self::BINN_UINT8, $binnString[$pos]); - - // Size - if ($sizeBytes & 1 << 7) { - $sizeBytes = $this->unpack(self::BINN_UINT32, substr($binnString, $pos, 4)); - $this->size = ($sizeBytes &~ (1 << 31)); // Cut bit - $pos += 4; - } else { - $this->size = $sizeBytes; - $pos += 1; - } - - unset($sizeBytes); - - $countBytes = $this->unpack(self::BINN_UINT8, $binnString[$pos]); - - // Size - if ($countBytes & 1 << 7) { - $countBytes = $this->unpack(self::BINN_UINT32, substr($binnString, $pos, 4)); - $this->count = ($countBytes &~ (1 << 31)); // Cut bit - $pos += 4; - } else { - $this->count = $countBytes; - $pos += 1; - } - - unset($countBytes); - - // Data - $stopWhile = false; - while ($pos < $this->size && !$stopWhile) { - $varKey = $this->unpack(self::BINN_INT32, substr($binnString, $pos, 4)); - $pos += 4; - - $varType = $this->unpack(self::BINN_UINT8, $binnString[$pos]); - $varStorageType = $this->storageType($varType); - $pos += 1; - - if ($varStorageType === self::BINN_STORAGE_QWORD - || $varStorageType === self::BINN_STORAGE_DWORD - || $varStorageType === self::BINN_STORAGE_WORD - || $varStorageType === self::BINN_STORAGE_BYTE - || $varStorageType === self::BINN_STORAGE_NOBYTES - ) { - $varSize = $this->getTypeSize($varType); - $val = $this->unpack($varType, substr($binnString, $pos, $varSize['data'])); - $this->_addVal($varKey, $varType, $val); - $pos += $varSize['data']; - - } else if ($varStorageType === self::BINN_STRING ) { - $stringSize = $this->unpack(self::BINN_UINT8, $binnString[$pos]); - - // Size - if ($stringSize & 1 << 7) { - $stringSize = $this->unpack(self::BINN_UINT32, substr($binnString, $pos, 4)); - $stringSize = ($stringSize &~ (1 << 31)); // Cut bit - $pos += 4; - } else { - $pos += 1; - } - - $this->_addVal($varKey,self::BINN_STRING, $this->unpack( - self::BINN_STRING, - substr($binnString, $pos, $stringSize) - )); - - $pos += $stringSize; - $pos += 1; // Null byte - } else if ($varStorageType === self::BINN_STORAGE_CONTAINER) { - $list_size = $this->unpack(self::BINN_UINT8, $binnString[$pos]);; - - // Size - if ($list_size & 1 << 7) { - $list_size = $this->unpack(self::BINN_UINT32, substr($binnString, $pos, 4)); - $list_size = ($list_size &~ (1 << 31)); // Cut bit - } - - $substring = substr($binnString, $pos-1, $list_size); - - foreach ($this->containersClasses as $containerType => $containersClass) { - if ($containerType === $varType) { - $container = new $containersClass($substring); - $this->_addVal($varKey, $varType, $container); - break; - } - } - - $pos += ($list_size-1); - } else { - $stopWhile = true; - } - } - } - - /** - * Get binary string - * - * @return string - */ - public function getBinnVal() - { - $this->calculateSize(); - - $this->binnString = ''; - $this->binnString .= $this->pack(self::BINN_UINT8, $this->binnType); - - $this->binnString .= $this->packSize($this->size); - - $count = count($this->binnArr); - $this->binnString .= $this->packSize($count); - - foreach ($this->binnArr as &$arr) { - $key = $arr[self::KEY_KEY]; - $type = $arr[self::KEY_TYPE]; - $storageType = $this->storageType($type); - - $this->binnString .= $this->pack(self::BINN_INT32, $key); - - if ($type === self::BINN_BOOL) { - $this->binnString .= $arr[self::KEY_VAL] - ? $this->packType(self::BINN_TRUE) - : $this->packType(self::BINN_FALSE); - - continue; - } - - if ($storageType === self::BINN_STORAGE_QWORD - || $storageType === self::BINN_STORAGE_DWORD - || $storageType === self::BINN_STORAGE_WORD - || $storageType === self::BINN_STORAGE_BYTE - ) { - $this->binnString .= $this->packType($arr[self::KEY_TYPE]); - $this->binnString .= $this->pack($arr[self::KEY_TYPE], $arr[self::KEY_VAL]); - } else if ($storageType === self::BINN_STORAGE_NOBYTES) { - $this->binnString .= $this->packType($arr[self::KEY_TYPE]); - } else if ($storageType === self::BINN_STORAGE_STRING) { - $this->binnString .= $this->packType(self::BINN_STRING); - $this->binnString .= $this->packSize($arr[self::KEY_SIZE]); - $this->binnString .= $this->pack(self::BINN_STRING, $arr[self::KEY_VAL]); - $this->binnString .= $this->pack(self::BINN_NULL); - } else if ($storageType === self::BINN_STORAGE_CONTAINER) { - $this->binnString .= $arr[self::KEY_VAL]->getBinnVal(); - } - } - - return $this->binnString; - } - - /** - * Check is valid array to serialize - * - * @param $array - * @return bool - */ - public static function validArray($array) - { - $array = (array)$array; - if (!self::isArrayAssoc($array)) { - return false; - } - - if (self::isArrayObject($array)) { - return false; - } - - return true; - } - - /** - * @param array $array - * @return string - */ - public function serialize($array = []) - { - if (empty($array)) { - return $this->getBinnVal(); - } - - $this->binnFree(); - - if (! $this->isArrayAssoc($array)) { - throw new InvalidArrayException('Array should be associative'); - } - - foreach ($array as $key => $item) { - $type = $this->detectType($item); - $storageType = $this->storageType($type); - - if ($storageType === self::BINN_STORAGE_CONTAINER) { - foreach ($this->containersClasses as $contanerType => $containersClass) - { - if ($containersClass::validArray($item)) { - $container = new $containersClass(); - $container->serialize($item); - $item = $container; - break; - } - } - } - - $this->_addVal($key, $type, $item); - } - - return $this->getBinnVal(); - } - - /** - * @param string $binnString - * @return array - */ - public function unserialize($binnString = '') - { - if (empty($binnString)) { - return $this->getBinnArr(); - } - - $this->binnFree(); - - $this->binnOpen($binnString); - return $this->getBinnArr(); + $this->items[$key] = $value; } -} \ No newline at end of file +} diff --git a/src/BinnObject.php b/src/BinnObject.php index a7ee8ee..5a563b6 100644 --- a/src/BinnObject.php +++ b/src/BinnObject.php @@ -2,298 +2,9 @@ namespace Knik\Binn; -use Knik\Binn\Exceptions\InvalidArrayException; +use Knik\Binn\Contracts\Container; -class BinnObject extends BinnAbstract +class BinnObject extends BinnMap { protected $binnType = self::BINN_OBJECT; - - public function __construct($binnString = '') - { - $this->binnClass = self::class; - - if ($binnString != '') { - $this->_binnLoad($binnString); - } - - return $this; - } - - /** - * @param string - */ - private function _binnLoad($binnString) - { - $pos = 1; // Position - $sizeBytes = $this->unpack(self::BINN_UINT8, $binnString[$pos]); - - // Size - if ($sizeBytes & 1 << 7) { - $sizeBytes = $this->unpack(self::BINN_UINT32, substr($binnString, $pos, 4)); - $this->size = ($sizeBytes &~ (1 << 31)); // Cut bit - $pos += 4; - } else { - $this->size = $sizeBytes; - $pos += 1; - } - - unset($sizeBytes); - - $countBytes = $this->unpack(self::BINN_UINT8, $binnString[$pos]); - - // Size - if ($countBytes & 1 << 7) { - $countBytes = $this->unpack(self::BINN_UINT32, substr($binnString, $pos, 4)); - $this->count = ($countBytes &~ (1 << 31)); // Cut bit - $pos += 4; - } else { - $this->count = $countBytes; - $pos += 1; - } - - unset($countBytes); - - // Data - $stopWhile = false; - while ($pos < $this->size && !$stopWhile) { - // Key size - $varKeySize = $this->unpack(self::BINN_UINT8, substr($binnString, $pos, 1)); - $pos += 1; - - $varKey = $this->unpack(self::BINN_STRING, substr($binnString, $pos, $varKeySize)); - $pos += $varKeySize; - - $varType = $this->unpack(self::BINN_UINT8, $binnString[$pos]); - $varStorageType = $this->storageType($varType); - $pos += 1; - - if ($varStorageType === self::BINN_STORAGE_QWORD - || $varStorageType === self::BINN_STORAGE_DWORD - || $varStorageType === self::BINN_STORAGE_WORD - || $varStorageType === self::BINN_STORAGE_BYTE - || $varStorageType === self::BINN_STORAGE_NOBYTES - ) { - $varSize = $this->getTypeSize($varType); - $val = $this->unpack($varType, substr($binnString, $pos, $varSize['data'])); - $this->_addVal($varKey, $varType, $val); - $pos += $varSize['data']; - - } else if ($varStorageType === self::BINN_STRING ) { - $stringSize = $this->unpack(self::BINN_UINT8, $binnString[$pos]); - - // Size - if ($stringSize & 1 << 7) { - $stringSize = $this->unpack(self::BINN_UINT32, substr($binnString, $pos, 4)); - $stringSize = ($stringSize &~ (1 << 31)); // Cut bit - $pos += 4; - } else { - $pos += 1; - } - - $this->_addVal($varKey,self::BINN_STRING, $this->unpack( - self::BINN_STRING, - substr($binnString, $pos, $stringSize) - )); - - $pos += $stringSize; - $pos += 1; // Null byte - } else if ($varStorageType === self::BINN_STORAGE_CONTAINER) { - $list_size = $this->unpack(self::BINN_UINT8, $binnString[$pos]);; - - // Size - if ($list_size & 1 << 7) { - $list_size = $this->unpack(self::BINN_UINT32, substr($binnString, $pos, 4)); - $list_size = ($list_size &~ (1 << 31)); // Cut bit - } - - $substring = substr($binnString, $pos-1, $list_size); - - foreach ($this->containersClasses as $containerType => $containersClass) { - if ($containerType === $varType) { - $container = new $containersClass($substring); - $this->_addVal($varKey, $varType, $container); - break; - } - } - - $pos += ($list_size-1); - } else { - $stopWhile = true; - } - } - } - - /** - * Get binary string - * - * @return string - */ - public function getBinnVal() - { - $this->calculateSize(); - - $this->binnString = ''; - $this->binnString .= $this->pack(self::BINN_UINT8, $this->binnType); - - $this->binnString .= $this->packSize($this->size); - - $count = count($this->binnArr); - $this->binnString .= $this->packSize($count); - - foreach ($this->binnArr as &$arr) { - $key = $arr[self::KEY_KEY]; - $type = $arr[self::KEY_TYPE]; - $storageType = $this->storageType($type); - - $this->binnString .= $this->pack(self::BINN_UINT8, mb_strlen($key)); - $this->binnString .= $this->pack(self::BINN_STRING, $key); - - if ($type === self::BINN_BOOL) { - $this->binnString .= $arr[self::KEY_VAL] - ? $this->packType(self::BINN_TRUE) - : $this->packType(self::BINN_FALSE); - - continue; - } - - if ($storageType === self::BINN_STORAGE_QWORD - || $storageType === self::BINN_STORAGE_DWORD - || $storageType === self::BINN_STORAGE_WORD - || $storageType === self::BINN_STORAGE_BYTE - ) { - $this->binnString .= $this->packType($arr[self::KEY_TYPE]); - $this->binnString .= $this->pack($arr[self::KEY_TYPE], $arr[self::KEY_VAL]); - } else if ($storageType === self::BINN_STORAGE_NOBYTES) { - $this->binnString .= $this->packType($arr[self::KEY_TYPE]); - } else if ($storageType === self::BINN_STORAGE_STRING) { - $this->binnString .= $this->packType(self::BINN_STRING); - $this->binnString .= $this->packSize($arr[self::KEY_SIZE]); - $this->binnString .= $this->pack(self::BINN_STRING, $arr[self::KEY_VAL]); - $this->binnString .= $this->pack(self::BINN_NULL); - } else if ($storageType === self::BINN_STORAGE_CONTAINER) { - $this->binnString .= $arr[self::KEY_VAL]->getBinnVal(); - } - } - - return $this->binnString; - } - - /** - * @param string $binnString - */ - public function binnOpen($binnString = '') - { - if ($binnString != '') { - $this->_binnLoad($binnString); - } - } - - /** - * @param integer $key - * @param int $type - * @param mixed $value - */ - private function _addVal($key, $type, $value) - { - if (in_array($type, - [self::BINN_INT64, self::BINN_INT32, self::BINN_INT16, - self::BINN_UINT64,self::BINN_UINT32, self::BINN_UINT16]) - ) { - $type = $this->compressInt($type, $value); - } - - $size = $this->getTypeSize($type, $value); - - $this->dataSize += $size['data']; - $this->metaSize += $size['meta']; - - // Key size. Size size + strlen - $this->metaSize += 1 + strlen($key); - - $this->count++; - - $this->binnArr[] = [ - self::KEY_TYPE => $type, - self::KEY_VAL => $value, - self::KEY_SIZE => $size['data'], - self::KEY_KEY => $key, - ]; - } - - /** - * Check is valid array to serialize - * - * @param $array - * @return bool - */ - public static function validArray($array) - { - $array = (array)$array; - - /* - if (count(array_filter(array_keys($array), 'is_string')) > 0) { - return true; - } - */ - - if (self::isArrayObject($array)) { - return true; - } - - return false; - } - - /** - * @param array $array - * @return string - */ - public function serialize($array = []) - { - if (empty($array)) { - return $this->getBinnVal(); - } - - $this->binnFree(); - - if (! $this->isArrayAssoc($array)) { - throw new InvalidArrayException('Array should be associative'); - } - - foreach ($array as $key => $item) { - $type = $this->detectType($item); - $storageType = $this->storageType($type); - - if ($storageType === self::BINN_STORAGE_CONTAINER) { - foreach ($this->containersClasses as $contanerType => $containersClass) - { - if ($containersClass::validArray($item)) { - $container = new $containersClass(); - $container->serialize($item); - $item = $container; - break; - } - } - } - - $this->_addVal($key, $type, $item); - } - - return $this->getBinnVal(); - } - - /** - * @param string $binnString - * @return array - */ - public function unserialize($binnString = '') - { - if (empty($binnString)) { - return $this->getBinnArr(); - } - - $this->binnFree(); - - $this->binnOpen($binnString); - return $this->getBinnArr(); - } -} \ No newline at end of file +} diff --git a/src/Contracts/BinnValueDecoder.php b/src/Contracts/BinnValueDecoder.php new file mode 100644 index 0000000..bb658dd --- /dev/null +++ b/src/Contracts/BinnValueDecoder.php @@ -0,0 +1,10 @@ +decoders = $decoders; + } + + public function decode($value, $format, $context = []) + { + /** @var BinnValueDecoder $decoder */ + foreach ($this->decoders->getAll() as $decoder) { + if ($decoder->supportsDecoding($value)) { + return $decoder->decode($value); + } + } + + return null; + } +} diff --git a/src/Decoder/Containers/BinnListDecoder.php b/src/Decoder/Containers/BinnListDecoder.php new file mode 100644 index 0000000..75a6223 --- /dev/null +++ b/src/Decoder/Containers/BinnListDecoder.php @@ -0,0 +1,71 @@ +decoders = $decoders; + } + + public function decode(string $bytes) + { + $readPosition = 1; + $totalSize = Unpacker::unpackSize8($bytes[$readPosition]); + + if ($totalSize > Binn::BINN_MAX_ONE_BYTE_SIZE) { + $totalSize = Unpacker::unpackSize32(substr($bytes, $readPosition, 4)); + $readPosition += 4; + } else { + $readPosition++; + } + + $totalItems = Unpacker::unpackSize8($bytes[$readPosition]); + + if ($totalItems > Binn::BINN_MAX_ONE_BYTE_SIZE) { + $totalItems = Unpacker::unpackSize32(substr($bytes, $readPosition, 4)); + $readPosition += 4; + } else { + $readPosition++; + } + + $readedItems = 0; + + $result = []; + + while ($readedItems < $totalItems && $readPosition < $totalSize) { + $readSize = $this->readSizeWithType(substr($bytes, $readPosition, 5)); + $innerStorage = substr($bytes, $readPosition, $readSize); + + /** @var BinnValueDecoder $decoder */ + foreach ($this->decoders->getAll() as $decoder) { + if ($decoder->supportsDecoding($innerStorage)) { + $result[] = $decoder->decode($innerStorage); + break; + } + } + + $readPosition += $readSize; + $readedItems++; + } + + return $result; + } + + public function supportsDecoding(string $bytes): bool + { + $type = $this->detectType($bytes); + + return $type === Binn::BINN_LIST; + } +} diff --git a/src/Decoder/Containers/BinnMapDecoder.php b/src/Decoder/Containers/BinnMapDecoder.php new file mode 100644 index 0000000..60571b2 --- /dev/null +++ b/src/Decoder/Containers/BinnMapDecoder.php @@ -0,0 +1,74 @@ +decoders = $decoders; + } + + public function decode(string $bytes) + { + $readPosition = 1; + $totalSize = Unpacker::unpackSize8($bytes[$readPosition]); + + if ($totalSize > Binn::BINN_MAX_ONE_BYTE_SIZE) { + $totalSize = Unpacker::unpackSize32(substr($bytes, $readPosition, 4)); + $readPosition += 4; + } else { + $readPosition++; + } + + $totalItems = Unpacker::unpackSize8($bytes[$readPosition]); + + if ($totalItems > Binn::BINN_MAX_ONE_BYTE_SIZE) { + $totalItems = Unpacker::unpackSize32(substr($bytes, $readPosition, 4)); + $readPosition += 4; + } else { + $readPosition++; + } + + $readedItems = 0; + + $result = []; + + while ($readedItems < $totalItems && $readPosition < $totalSize) { + $keyValue = Unpacker::unpackInt32(substr($bytes, $readPosition, 4)); + $readPosition += 4; + + $readSize = $this->readSizeWithType(substr($bytes, $readPosition, 4)); + $innerStorage = substr($bytes, $readPosition, $readSize); + + /** @var BinnValueDecoder $decoder */ + foreach ($this->decoders->getAll() as $decoder) { + if ($decoder->supportsDecoding($innerStorage)) { + $result[$keyValue] = $decoder->decode($innerStorage); + break; + } + } + + $readPosition += $readSize; + $readedItems++; + } + + return $result; + } + + public function supportsDecoding(string $bytes): bool + { + $type = $this->detectType($bytes); + + return $type === Binn::BINN_MAP; + } +} diff --git a/src/Decoder/Containers/BinnObjectDecoder.php b/src/Decoder/Containers/BinnObjectDecoder.php new file mode 100644 index 0000000..15661e7 --- /dev/null +++ b/src/Decoder/Containers/BinnObjectDecoder.php @@ -0,0 +1,76 @@ +decoders = $decoders; + } + + public function decode(string $bytes) + { + $readPosition = 1; + $totalSize = Unpacker::unpackSize8($bytes[$readPosition]); + + if ($totalSize > Binn::BINN_MAX_ONE_BYTE_SIZE) { + $totalSize = Unpacker::unpackSize32(substr($bytes, $readPosition, 4)); + $readPosition += 4; + } else { + $readPosition++; + } + + $totalItems = Unpacker::unpackSize8($bytes[$readPosition]); + + if ($totalItems > Binn::BINN_MAX_ONE_BYTE_SIZE) { + $totalItems = Unpacker::unpackSize32(substr($bytes, $readPosition, 4)); + $readPosition += 4; + } else { + $readPosition++; + } + + $readedItems = 0; + + $result = []; + + while ($readedItems < $totalItems && $readPosition < $totalSize) { + $keySize = Unpacker::unpackSize8($bytes[$readPosition]); + $readPosition++; + $keyValue = Unpacker::unpackString(substr($bytes, $readPosition, $keySize)); + $readPosition += $keySize; + + $readSize = $this->readSizeWithType(substr($bytes, $readPosition, 4)); + $innerStorage = substr($bytes, $readPosition, $readSize); + + /** @var BinnValueDecoder $decoder */ + foreach ($this->decoders->getAll() as $decoder) { + if ($decoder->supportsDecoding($innerStorage)) { + $result[$keyValue] = $decoder->decode($innerStorage); + break; + } + } + + $readPosition += $readSize; + $readedItems++; + } + + return $result; + } + + public function supportsDecoding(string $bytes): bool + { + $type = $this->detectType($bytes); + + return $type === Binn::BINN_OBJECT; + } +} diff --git a/src/Decoder/Decoder.php b/src/Decoder/Decoder.php new file mode 100644 index 0000000..14b123c --- /dev/null +++ b/src/Decoder/Decoder.php @@ -0,0 +1,83 @@ +detectType($bytes) & ~ Binn::BINN_TYPE_MASK); + + switch ($type) { + case Binn::BINN_STORAGE_NOBYTES: + return 1; + case Binn::BINN_STORAGE_BYTE: + return 2; + case Binn::BINN_STORAGE_WORD: + return 3; + case Binn::BINN_STORAGE_DWORD: + return 5; + case Binn::BINN_STORAGE_QWORD: + return 9; + case Binn::BINN_STORAGE_STRING: + return $this->readSizeStringWithType($bytes); + case Binn::BINN_STORAGE_BLOB: + return $this->readSizeBlobWithType($bytes); + case Binn::BINN_STORAGE_CONTAINER: + return $this->readSizeContainerWithType($bytes); + } + + return 0; + } + + private function readSizeStringWithType(string $bytes): int + { + // type, size size, string size, null terminator + return $this->readSizeBlobWithType($bytes) + 1; + } + + private function readSizeBlobWithType(string $bytes): int + { + $size = Unpacker::unpackSize8($bytes[1]); + $sizeSize = 1; + + if ($size > Binn::BINN_MAX_ONE_BYTE_SIZE) { + $sizeBytes = substr($bytes, 1, 4); + $size = Unpacker::unpackSize32($sizeBytes); + $sizeSize = 4; + } + + // type, size size, data size + return $size + $sizeSize + 1; + } + + private function readSizeContainerWithType(string $bytes): int + { + $size = Unpacker::unpackSize8($bytes[1]); + + if ($size > Binn::BINN_MAX_ONE_BYTE_SIZE) { + $sizeBytes = substr($bytes, 1, 4); + $size = Unpacker::unpackSize32($sizeBytes); + } + + return $size; + } +} diff --git a/src/Decoder/DecoderCollection.php b/src/Decoder/DecoderCollection.php new file mode 100644 index 0000000..504720b --- /dev/null +++ b/src/Decoder/DecoderCollection.php @@ -0,0 +1,37 @@ +decoders; + } + + public function findByType(int $type): ?BinnValueDecoder + { + if (array_key_exists($type, $this->mapper)) { + return $this->mapper[$type]; + } + + return null; + } + + public function add(int $type, BinnValueDecoder $decoder): void + { + if (!in_array($decoder, $this->decoders, true)) { + $this->decoders[] = $decoder; + } + + $this->mapper[$type] = $decoder; + } +} diff --git a/src/Decoder/DecoderCollectionFactory.php b/src/Decoder/DecoderCollectionFactory.php new file mode 100644 index 0000000..89a52dc --- /dev/null +++ b/src/Decoder/DecoderCollectionFactory.php @@ -0,0 +1,38 @@ +add(Binn::BINN_TRUE, $simpleStorageValueDecoder); + $decoderCollection->add(Binn::BINN_FALSE, $simpleStorageValueDecoder); + $decoderCollection->add(Binn::BINN_UINT8, $simpleStorageValueDecoder); + $decoderCollection->add(Binn::BINN_INT8, $simpleStorageValueDecoder); + $decoderCollection->add(Binn::BINN_UINT16, $simpleStorageValueDecoder); + $decoderCollection->add(Binn::BINN_INT16, $simpleStorageValueDecoder); + $decoderCollection->add(Binn::BINN_UINT32, $simpleStorageValueDecoder); + $decoderCollection->add(Binn::BINN_INT32, $simpleStorageValueDecoder); + $decoderCollection->add(Binn::BINN_UINT64, $simpleStorageValueDecoder); + $decoderCollection->add(Binn::BINN_INT64, $simpleStorageValueDecoder); + $decoderCollection->add(Binn::BINN_STRING, $simpleStorageValueDecoder); + $decoderCollection->add(Binn::BINN_FLOAT32, $simpleStorageValueDecoder); + $decoderCollection->add(Binn::BINN_FLOAT64, $simpleStorageValueDecoder); + + $decoderCollection->add(Binn::BINN_LIST, new BinnListDecoder($decoderCollection)); + $decoderCollection->add(Binn::BINN_MAP, new BinnMapDecoder($decoderCollection)); + $decoderCollection->add(Binn::BINN_OBJECT, new BinnObjectDecoder($decoderCollection)); + + return $decoderCollection; + } +} diff --git a/src/Decoder/SimpleStorageValueDecoder.php b/src/Decoder/SimpleStorageValueDecoder.php new file mode 100644 index 0000000..b068448 --- /dev/null +++ b/src/Decoder/SimpleStorageValueDecoder.php @@ -0,0 +1,120 @@ +detectType($bytes); + + switch ($type) { + case Binn::BINN_NULL: + return null; + + case Binn::BINN_TRUE: + return true; + + case Binn::BINN_FALSE: + return false; + + case Binn::BINN_FLOAT32: + return Unpacker::unpackFloat32( + substr($bytes, 1, 4) + ); + + case Binn::BINN_FLOAT64: + return Unpacker::unpackFloat64( + substr($bytes, 1, 8) + ); + + case Binn::BINN_INT64: + return Unpacker::unpackInt64( + substr($bytes, 1, 8) + ); + + case Binn::BINN_INT32: + return Unpacker::unpackInt32( + substr($bytes, 1, 4) + ); + + case Binn::BINN_INT16: + return Unpacker::unpackInt16( + substr($bytes, 1, 2) + ); + + case Binn::BINN_INT8: + return Unpacker::unpackInt8( + $bytes[1] + ); + + case Binn::BINN_UINT64: + return Unpacker::unpackUint64( + substr($bytes, 1, 8) + ); + + case Binn::BINN_UINT32: + return Unpacker::unpackUint32( + substr($bytes, 1, 4) + ); + + case Binn::BINN_UINT16: + return Unpacker::unpackUint16( + substr($bytes, 1, 2) + ); + + case Binn::BINN_UINT8: + return Unpacker::unpackUint8($bytes[1]); + + case Binn::BINN_STRING: + return $this->decodeString($bytes); + + case Binn::BINN_STORAGE_BLOB: + return $this->decodeString($bytes); + } + + return null; + } + + public function supportsDecoding(string $bytes): bool + { + $type = $this->detectType($bytes); + + return in_array($type, [ + Binn::BINN_NULL, + Binn::BINN_TRUE, + Binn::BINN_FALSE, + Binn::BINN_FLOAT32, + Binn::BINN_FLOAT64, + Binn::BINN_INT64, + Binn::BINN_INT32, + Binn::BINN_INT16, + Binn::BINN_INT8, + Binn::BINN_UINT64, + Binn::BINN_UINT32, + Binn::BINN_UINT16, + Binn::BINN_UINT8, + Binn::BINN_STRING, + Binn::BINN_STORAGE_BLOB, + ], true); + } + + private function decodeString(string $bytes): string + { + $size = Unpacker::unpackSize8($bytes[1]); + $offset = 2; + + if ($size > Binn::BINN_MAX_ONE_BYTE_SIZE) { + $sizeBytes = substr($bytes, 1, 4); + $size = Unpacker::unpackSize32($sizeBytes); + $offset = 5; + } + + $stringBytes = substr($bytes, $offset, $size); + + return Unpacker::unpackString($stringBytes); + } +} diff --git a/src/Decoder/Unpacker.php b/src/Decoder/Unpacker.php new file mode 100644 index 0000000..efcd93b --- /dev/null +++ b/src/Decoder/Unpacker.php @@ -0,0 +1,87 @@ +encoders = $encoders; + } + + public function encode($value, $format, $context = []): ?string + { + /** @var BinnValueEncoder $encoder */ + foreach ($this->encoders->getAll() as $encoder) { + if ($encoder->supportsEncoding($value)) { + return $encoder->encode($value); + } + } + + return "\x00"; + } +} diff --git a/src/Encoder/BinnEncoder.php b/src/Encoder/BinnEncoder.php new file mode 100644 index 0000000..74d6d8d --- /dev/null +++ b/src/Encoder/BinnEncoder.php @@ -0,0 +1,53 @@ +encodingImpl = $encodingImpl ?: new BinnEncode( + (new EncoderCollectionFactory())->getCollection() + ); + + $this->decodingImpl = $decodingImpl ?: new BinnDecode( + (new DecoderCollectionFactory())->getCollection() + ); + } + + public function decode(string $data, string $format, array $context = []) + { + return $this->decodingImpl->decode($data, $format, $context); + } + + public function encode($data, string $format, array $context = []) + { + return $this->encodingImpl->encode($data, $format, $context); + } + + /** + * {@inheritdoc} + */ + public function supportsEncoding(string $format) + { + return self::FORMAT === $format; + } + + /** + * {@inheritdoc} + */ + public function supportsDecoding(string $format) + { + return self::FORMAT === $format; + } +} diff --git a/src/Encoder/BlobEncode.php b/src/Encoder/BlobEncode.php new file mode 100644 index 0000000..b6fd0ac --- /dev/null +++ b/src/Encoder/BlobEncode.php @@ -0,0 +1,35 @@ +supportsEncoding($value)) { + throw new BinnException('Invalid value. Resource expected'); + } + + $contents = ''; + + while (!feof($value)) { + $contents .= fread($value, 1024); + } + + $encodedType = Packer::packUint8(self::TYPE); + $encodedSize = Packer::packSize(strlen($encodedType) + strlen($contents), true); + + return $encodedType . $encodedSize . $contents; + } + + public function supportsEncoding($value): bool + { + return is_resource($value); + } +} diff --git a/src/Encoder/Containers/BinnListEncoder.php b/src/Encoder/Containers/BinnListEncoder.php new file mode 100644 index 0000000..6676262 --- /dev/null +++ b/src/Encoder/Containers/BinnListEncoder.php @@ -0,0 +1,69 @@ +encoders = $encoders; + } + + public function encode($value): string + { + if ($this->isArrayAssoc($value)) { + throw new InvalidArrayException('Array should be sequential'); + } + + $encodedItems = ''; + + foreach ($value as $item) { + /** @var BinnValueEncoder $encoder */ + foreach ($this->encoders->getAll() as $encoder) { + if ($encoder->supportsEncoding($item)) { + $encodedItems .= $encoder->encode($item); + break; + } + } + } + + $encodedType = Packer::packUint8(self::TYPE); + $encodedCount = Packer::packSize(count($value)); + $encodedSize = Packer::packSize( + strlen($encodedType) + strlen($encodedCount) + strlen($encodedItems), + true + ); + + return $encodedType + . $encodedSize + . $encodedCount + . $encodedItems; + } + + public function supportsEncoding($value): bool + { + return is_array($value) && !$this->isArrayAssoc($value); + } + + private function isArrayAssoc($arr) + { + $arr = (array)$arr; + + if ([] === $arr) { + return false; + } + + return array_keys($arr) !== range(0, count($arr) - 1); + } +} diff --git a/src/Encoder/Containers/BinnMapEncoder.php b/src/Encoder/Containers/BinnMapEncoder.php new file mode 100644 index 0000000..52a0ef3 --- /dev/null +++ b/src/Encoder/Containers/BinnMapEncoder.php @@ -0,0 +1,76 @@ +encoders = $encoders; + } + + public function encode($value): string + { + if (!$this->isArrayKeyNumbers($value)) { + throw new InvalidArrayException('Array keys should be numbers'); + } + + $encodedData = ''; + + foreach ($value as $key => $item) { + /** @var BinnValueEncoder $encoder */ + foreach ($this->encoders->getAll() as $encoder) { + if ($encoder->supportsEncoding($item)) { + $encodedData .= Packer::packInt32($key); + $encodedData .= $encoder->encode($item); + break; + } + } + } + + $encodedType = Packer::packUint8(self::TYPE); + $encodedCount = Packer::packSize(count($value)); + $encodedSize = Packer::packSize( + strlen($encodedType) + strlen($encodedCount) + strlen($encodedData), + true + ); + + return $encodedType + . $encodedSize + . $encodedCount + . $encodedData; + } + + public function supportsEncoding($value): bool + { + return is_array($value) && $this->isArrayKeyNumbers($value); + } + + private function isArrayKeyNumbers($arr): bool + { + $arr = (array)$arr; + + if ([] === $arr) { + return false; + } + + foreach (array_keys($arr) as $key) { + if (!is_int($key)) { + return false; + } + } + + return true; + } +} diff --git a/src/Encoder/Containers/BinnObjectEncoder.php b/src/Encoder/Containers/BinnObjectEncoder.php new file mode 100644 index 0000000..07ab2e6 --- /dev/null +++ b/src/Encoder/Containers/BinnObjectEncoder.php @@ -0,0 +1,57 @@ +encoders = $encoders; + } + + public function encode($value): string + { + $encodedData = ''; + + $count = 0; + foreach ($value as $key => $item) { + $count++; + /** @var BinnValueEncoder $encoder */ + foreach ($this->encoders->getAll() as $encoder) { + if ($encoder->supportsEncoding($item)) { + $encodedData .= Packer::packUint8(strlen($key)); + $encodedData .= Packer::packString($key); + $encodedData .= $encoder->encode($item); + break; + } + } + } + + $encodedType = Packer::packUint8(self::TYPE); + $encodedCount = Packer::packSize($count); + $encodedSize = Packer::packSize( + strlen($encodedType) + strlen($encodedCount) + strlen($encodedData), + true + ); + + return $encodedType + . $encodedSize + . $encodedCount + . $encodedData; + } + + public function supportsEncoding($value): bool + { + return is_array($value) || is_object($value); + } +} diff --git a/src/Encoder/EncoderCollection.php b/src/Encoder/EncoderCollection.php new file mode 100644 index 0000000..3487cf9 --- /dev/null +++ b/src/Encoder/EncoderCollection.php @@ -0,0 +1,37 @@ +encoders; + } + + public function findByType(int $type): ?BinnValueEncoder + { + if (array_key_exists($type, $this->mapper)) { + return $this->mapper[$type]; + } + + return null; + } + + public function add(int $type, BinnValueEncoder $encoder): void + { + if (!in_array($encoder, $this->encoders, true)) { + $this->encoders[] = $encoder; + } + + $this->mapper[$type] = $encoder; + } +} diff --git a/src/Encoder/EncoderCollectionFactory.php b/src/Encoder/EncoderCollectionFactory.php new file mode 100644 index 0000000..3ea949a --- /dev/null +++ b/src/Encoder/EncoderCollectionFactory.php @@ -0,0 +1,40 @@ +add(Binn::BINN_TRUE, $simpleTypeEncoder); + $encoderCollection->add(Binn::BINN_FALSE, $simpleTypeEncoder); + $encoderCollection->add(Binn::BINN_UINT8, $simpleTypeEncoder); + $encoderCollection->add(Binn::BINN_INT8, $simpleTypeEncoder); + $encoderCollection->add(Binn::BINN_UINT16, $simpleTypeEncoder); + $encoderCollection->add(Binn::BINN_INT16, $simpleTypeEncoder); + $encoderCollection->add(Binn::BINN_UINT32, $simpleTypeEncoder); + $encoderCollection->add(Binn::BINN_INT32, $simpleTypeEncoder); + $encoderCollection->add(Binn::BINN_UINT64, $simpleTypeEncoder); + $encoderCollection->add(Binn::BINN_INT64, $simpleTypeEncoder); + $encoderCollection->add(Binn::BINN_STRING, $simpleTypeEncoder); + $encoderCollection->add(Binn::BINN_FLOAT32, $simpleTypeEncoder); + $encoderCollection->add(Binn::BINN_FLOAT64, $simpleTypeEncoder); + + $encoderCollection->add(Binn::BINN_STORAGE_BLOB, new BlobEncode()); + + $encoderCollection->add(Binn::BINN_LIST, new BinnListEncoder($encoderCollection)); + $encoderCollection->add(Binn::BINN_MAP, new BinnMapEncoder($encoderCollection)); + $encoderCollection->add(Binn::BINN_OBJECT, new BinnObjectEncoder($encoderCollection)); + + return $encoderCollection; + } +} diff --git a/src/Encoder/Packer.php b/src/Encoder/Packer.php new file mode 100644 index 0000000..9c36896 --- /dev/null +++ b/src/Encoder/Packer.php @@ -0,0 +1,91 @@ +detectType($value); + + return $this->encodeType($type) . $this->encodeValue($type, $value); + } + + public function encodeValue(int $type, $value = null): ?string + { + if ($type === Binn::BINN_NULL) { + return ''; + } + + if ($type === Binn::BINN_TRUE) { + return ''; + } + + if ($type === Binn::BINN_FALSE) { + return ''; + } + + if ($type === Binn::BINN_UINT64) { + return Packer::packUint64($value); + } + + if ($type === Binn::BINN_UINT32) { + return Packer::packUint32($value); + } + + if ($type === Binn::BINN_UINT16) { + return Packer::packUint16($value); + } + + if ($type === Binn::BINN_UINT8) { + return Packer::packUint8($value); + } + + if ($type === Binn::BINN_INT8) { + return Packer::packInt8($value); + } + + if ($type === Binn::BINN_INT16) { + return Packer::packInt16($value); + } + + if ($type === Binn::BINN_INT32) { + return Packer::packInt32($value); + } + + if ($type === Binn::BINN_INT64) { + return Packer::packInt64($value); + } + + if ($type === Binn::BINN_FLOAT32) { + return Packer::packFloat32($value); + } + + if ($type === Binn::BINN_FLOAT64) { + return Packer::packFloat64($value); + } + + if ($type === Binn::BINN_STRING) { + return Packer::packSize(strlen($value)) . Packer::packString($value) . "\x00"; + } + + return null; + } + + public function supportsEncoding($value): bool + { + return $this->detectType($value) !== null; + } + + public function encodeType(int $type): ?string + { + return $this->encodeValue(Binn::BINN_UINT8, $type); + } + + private function detectType($value): ?int + { + if (is_bool($value)) { + return $value ? Binn::BINN_TRUE : Binn::BINN_FALSE; + } + + if (is_string($value)) { + return Binn::BINN_STRING; + } + + if (is_int($value)) { + return $this->detectInt($value); + } + + if (is_float($value)) { + if (strlen($value) > 4) { + return Binn::BINN_FLOAT64; + } + + return Binn::BINN_FLOAT32; + } + + if (is_null($value)) { + return Binn::BINN_NULL; + } + + return null; + } + + public function detectInt($value): int + { + if ($value < 0) { + // int + if ($value >= Binn::INT8_MIN) { + return Binn::BINN_INT8; + } + + if ($value >= Binn::INT16_MIN) { + return Binn::BINN_INT16; + } + + if ($value >= Binn::INT32_MIN) { + return Binn::BINN_INT32; + } + + return Binn::BINN_INT64; + } + + // uint + if ($value <= Binn::UINT8_MAX) { + return Binn::BINN_UINT8; + } + + if ($value <= Binn::UINT16_MAX) { + return Binn::BINN_UINT16; + } + + if ($value <= Binn::UINT32_MAX) { + return Binn::BINN_UINT32; + } + + return Binn::BINN_UINT64; + } +} diff --git a/src/Exceptions/BinnException.php b/src/Exceptions/BinnException.php new file mode 100644 index 0000000..46c247e --- /dev/null +++ b/src/Exceptions/BinnException.php @@ -0,0 +1,10 @@ + - */ -class BinnAbstractTest extends TestCase -{ - public function testStorageType() - { - $binn = new BinnOver(); - - $this->assertEquals($binn::BINN_STORAGE_BYTE, $binn->storageType($binn::BINN_UINT8)); - $this->assertEquals($binn::BINN_STORAGE_WORD, $binn->storageType($binn::BINN_UINT16)); - $this->assertEquals($binn::BINN_STORAGE_DWORD, $binn->storageType($binn::BINN_UINT32)); - $this->assertEquals($binn::BINN_STORAGE_QWORD, $binn->storageType($binn::BINN_UINT64)); - $this->assertEquals($binn::BINN_STORAGE_STRING, $binn->storageType($binn::BINN_STRING)); - - $this->assertEquals($binn::BINN_STORAGE_CONTAINER, $binn->storageType($binn::BINN_LIST)); - $this->assertEquals($binn::BINN_STORAGE_CONTAINER, $binn->storageType($binn::BINN_MAP)); - $this->assertEquals($binn::BINN_STORAGE_CONTAINER, $binn->storageType($binn::BINN_OBJECT)); - } - - public function testDetectType() - { - $binn = new BinnOver(); - - $this->assertEquals($binn::BINN_TRUE, $binn->detectType(true)); - $this->assertEquals($binn::BINN_FALSE, $binn->detectType(false)); - - $this->assertEquals($binn::BINN_FLOAT32, $binn->detectType(1.25)); - $this->assertEquals($binn::BINN_FLOAT64, $binn->detectType(31.00000542123925)); - - $this->assertEquals($binn::BINN_NULL, $binn->detectType(null)); - } - - public function testDetectInt() - { - $binn = new BinnOver(); - - $this->assertEquals($binn::BINN_UINT8, $binn->detectInt(0)); - $this->assertEquals($binn::BINN_UINT8, $binn->detectInt(1)); - $this->assertEquals($binn::BINN_UINT8, $binn->detectInt(2)); - $this->assertEquals($binn::BINN_UINT8, $binn->detectInt(255)); - $this->assertEquals($binn::BINN_INT8, $binn->detectInt(-1)); - $this->assertEquals($binn::BINN_INT8, $binn->detectInt(-2)); - $this->assertEquals($binn::BINN_UINT16, $binn->detectInt(256)); - $this->assertEquals($binn::BINN_INT16, $binn->detectInt(-250)); - $this->assertEquals($binn::BINN_UINT32, $binn->detectInt(4294967295)); - $this->assertEquals($binn::BINN_INT32, $binn->detectInt(-2147483648)); - $this->assertEquals($binn::BINN_INT64, $binn->detectInt(-4294967295)); - $this->assertEquals($binn::BINN_UINT64, $binn->detectInt(18446744073709551615)); - $this->assertEquals($binn::BINN_INT64, $binn->detectInt(-9223372036854775808)); - } - - public function testCompressInt() - { - $binn = new BinnOver(); - - $this->assertEquals($binn::BINN_UINT8, $binn->compressInt($binn::BINN_UINT16, 1)); - - // Int -> Uint - $this->assertEquals($binn::BINN_UINT64, $binn->compressInt($binn::BINN_INT64, $binn::INT64_MAX)); - $this->assertEquals($binn::BINN_UINT32, $binn->compressInt($binn::BINN_INT32, $binn::INT32_MAX)); - $this->assertEquals($binn::BINN_UINT16, $binn->compressInt($binn::BINN_INT16, $binn::INT16_MAX)); - $this->assertEquals($binn::BINN_UINT8, $binn->compressInt($binn::BINN_INT8, $binn::INT8_MAX)); - - // Int -> int low - $this->assertEquals($binn::BINN_INT32, $binn->compressInt($binn::BINN_INT64, $binn::INT32_MIN)); - $this->assertEquals($binn::BINN_INT16, $binn->compressInt($binn::BINN_INT32, $binn::INT16_MIN)); - $this->assertEquals($binn::BINN_INT8, $binn->compressInt($binn::BINN_INT16, $binn::INT8_MIN)); - } - - public function testIsArrayAssoc() - { - $this->assertTrue(BinnOver::isArrayAssoc(['hello' => 'world'])); - } - - public function testPack() - { - $binn = new BinnOver(); - - $this->assertEquals("\x01", $binn->pack($binn::BINN_TRUE)); - $this->assertEquals("\x02", $binn->pack($binn::BINN_FALSE)); - - - $this->assertNull($binn->pack('Unknown', 'Unknown')); - } - - public function testGetTypeSize() - { - $binn = new BinnOver(); - - $this->assertEquals(['meta' => 1, 'data' => 0], $binn->getTypeSize($binn::BINN_TRUE)); - $this->assertEquals(['meta' => 1, 'data' => 0], $binn->getTypeSize($binn::BINN_FALSE)); - $this->assertEquals(['meta' => 3, 'data' => 1], $binn->getTypeSize($binn::BINN_STRING, 'a')); - $this->assertEquals(['meta' => 3, 'data' => 3], $binn->getTypeSize($binn::BINN_STRING, 'abc')); - $this->assertEquals(['meta' => 6, 'data' => 256], $binn->getTypeSize($binn::BINN_STRING, str_repeat('a', 256))); - $this->assertEquals(['meta' => 2, 'data' => 1], $binn->getTypeSize($binn::BINN_STORAGE_BLOB, 'a')); - $this->assertEquals(['meta' => 2, 'data' => 2], $binn->getTypeSize($binn::BINN_STORAGE_BLOB, 'ab')); - } -} - -// Make protected methods public -class BinnOver extends BinnList -{ - public function storageType($type) - { - return parent::storageType($type); - } - - public function detectType($value = null) - { - return parent::detectType($value); - } - - public function detectInt($value) - { - return parent::detectInt($value); - } - - public function compressInt($type, $val) - { - return parent::compressInt($type, $val); - } - - public static function isArrayAssoc($arr) - { - return parent::isArrayAssoc($arr); - } - - public function pack($varType, $value = null) - { - return parent::pack($varType, $value); - } - - public function unpack($varType, $value = null) - { - return parent::unpack($varType, $value); - } - - public function getTypeSize($varType, $value = '') - { - return parent::getTypeSize($varType, $value); - } -} \ No newline at end of file diff --git a/tests/BinnTestCase.php b/tests/BinnTestCase.php new file mode 100644 index 0000000..4a68e1f --- /dev/null +++ b/tests/BinnTestCase.php @@ -0,0 +1,55 @@ +getCollection()); + } + + protected function getDecoder(): BinnDecode + { + $decoderFactory = new DecoderCollectionFactory(); + return new BinnDecode($decoderFactory->getCollection()); + } +} diff --git a/tests/Files/file.jpg b/tests/Files/file.jpg new file mode 100644 index 0000000..d08c761 Binary files /dev/null and b/tests/Files/file.jpg differ diff --git a/tests/Files/file.txt b/tests/Files/file.txt new file mode 100644 index 0000000..ce01362 --- /dev/null +++ b/tests/Files/file.txt @@ -0,0 +1 @@ +hello diff --git a/tests/BinnListTest.php b/tests/Unit/BinnListTest.php similarity index 57% rename from tests/BinnListTest.php rename to tests/Unit/BinnListTest.php index 98bd2c7..c23be0c 100644 --- a/tests/BinnListTest.php +++ b/tests/Unit/BinnListTest.php @@ -1,26 +1,25 @@ - */ class BinnListTest extends TestCase { static private $stringBinnList = "\xE0\x15\x02\xA0\x05Hello\x00\xA0\x07 World!\x00"; + // https://github.com/liteserver/binn/blob/master/spec.md#a-list-of-3-integers public function testListInt() { $binn = new BinnList(); - - // https://github.com/liteserver/binn/blob/master/spec.md#a-list-of-3-integers $binn->addUint16(123)->addInt16(-456)->addUint16(789); - $this->assertEquals("\xE0\x0B\x03\x20\x7B\x41\xFE\x38\x40\x03\x15", $binn->getBinnVal()); + $result = $binn->getBinnVal(); - // 11 bytes - $this->assertEquals(11, $binn->binnSize()); + Assert::assertEquals("\xE0\x0B\x03\x20\x7B\x41\xFE\x38\x40\x03\x15", $result); + Assert::assertEquals(11, $binn->binnSize()); } public function testListFloat() @@ -30,29 +29,31 @@ public function testListFloat() $binn->addFloat($float); $binnString = $binn->getBinnVal(); - $binnRead = new BinnList($binnString); + $binnRead = new BinnList(); + $binnRead->binnOpen($binnString); $arrRead = $binnRead->getBinnArr(); - $this->assertEquals($float, $arrRead[0], '', 0.000001); + Assert::assertEqualsWithDelta($float, $arrRead[0], 0.000001); $double = 0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000025; $binn = new BinnList(); $binn->addDouble($double); $binnString = $binn->getBinnVal(); - $binnRead = new BinnList($binnString); + $binnRead = new BinnList(); + $binnRead->binnOpen($binnString); $arrRead = $binnRead->getBinnArr(); - $this->assertEquals($double, $arrRead[0]); + Assert::assertEqualsWithDelta($double, $arrRead[0], 0.000001); } public function testListString() { $binn = new BinnList(); $binn->addStr("Hello")->addStr(' World!'); - $this->assertEquals(self::$stringBinnList, $binn->getBinnVal()); + Assert::assertEquals(self::$stringBinnList, $binn->getBinnVal()); - $this->assertEquals(strlen($binn->getBinnVal()), $binn->binnSize()); + Assert::assertEquals(strlen($binn->getBinnVal()), $binn->binnSize()); } @@ -60,46 +61,46 @@ public function testListList() { $binn = new BinnList(); $binn->addStr("Hello"); - $binnSubj = new BinnList(); $binnSubj->addStr("World"); - $binn->addList($binnSubj); - $this->assertEquals("\xE0\x16\x02\xA0\x05Hello\x00\xE0\x0B\x01\xA0\x05World\x00", $binn->getBinnVal()); + $result = $binn->getBinnVal(); + + Assert::assertEquals("\xE0\x16\x02\xA0\x05Hello\x00\xE0\x0B\x01\xA0\x05World\x00", $result); } public function testBinnFree() { $binn = new BinnList(); - $binn->addUint8(123)->addInt16(-456)->addUint16(789); - $this->assertEquals("\xE0\x0B\x03\x20\x7B\x41\xFE\x38\x40\x03\x15", $binn->getBinnVal()); - $this->assertEquals(11, $binn->binnSize()); + Assert::assertEquals("\xE0\x0B\x03\x20\x7B\x41\xFE\x38\x40\x03\x15", $binn->getBinnVal()); + Assert::assertEquals(11, $binn->binnSize()); $binn->binnFree(); $binn->addUint8(512)->addInt16(-521); - $this->assertEquals("\xE0\x08\x02\x20\x00\x41\xFD\xF7", $binn->getBinnVal()); - $this->assertEquals(8, $binn->binnSize()); + + Assert::assertEquals("\xE0\x09\x02\x40\x02\x00\x41\xFD\xF7", $binn->getBinnVal()); + Assert::assertEquals(9, $binn->binnSize()); } public function testBinnOpen() { $binn = new BinnList(); $binn->binnOpen("\xE0\x15\x02\xA0\x05Hello\x00\xA0\x07 World!\x00"); - $this->assertEquals(['Hello', ' World!'], $binn->getBinnArr()); + Assert::assertEquals(['Hello', ' World!'], $binn->getBinnArr()); $binn->binnFree(); $binn->binnOpen("\xE0\x0B\x03\x20\x7B\x41\xFE\x38\x40\x03\x15"); - $this->assertEquals([123, -456, 789], $binn->getBinnArr()); + Assert::assertEquals([123, -456, 789], $binn->getBinnArr()); } public function testGetBinnArr() { $binn = new BinnList(); $binn->addUint8(123)->addInt16(-456)->addUint16(789); - $this->assertEquals([123, -456, 789], $binn->getBinnArr()); - $this->assertEquals([123, -456, 789], $binn->getBinnArr()); + Assert::assertEquals([123, -456, 789], $binn->getBinnArr()); + Assert::assertEquals([123, -456, 789], $binn->getBinnArr()); } public function testBigBinn() @@ -115,47 +116,48 @@ public function testBigBinn() $arr = $binn1->getBinnArr(); $binnString = $binn1->getBinnVal(); - $binn2 = new BinnList($binnString); + $binn2 = new BinnList(); + $binn2->binnOpen($binnString); $arr2 = $binn2->getBinnArr(); - $this->assertEquals($arr, $arr2); + Assert::assertEquals($arr, $arr2); } public function testUnserialize() { $binn = new BinnList(); - $this->assertEquals(['Hello', ' World!'], $binn->unserialize("\xE0\x15\x02\xA0\x05Hello\x00\xA0\x07 World!\x00")); + Assert::assertEquals(['Hello', ' World!'], $binn->unserialize("\xE0\x15\x02\xA0\x05Hello\x00\xA0\x07 World!\x00")); $binn = new BinnList(); $binn->binnOpen("\xE0\x15\x02\xA0\x05Hello\x00\xA0\x07 World!\x00"); - $this->assertEquals(['Hello', ' World!'], $binn->unserialize()); + Assert::assertEquals(['Hello', ' World!'], $binn->unserialize()); } public function testSerialize() { $binn = new BinnList(); $binnString = $binn->serialize(['Hello', ' World!']); - $this->assertEquals(self::$stringBinnList, $binnString); + Assert::assertEquals(self::$stringBinnList, $binnString); $binnString = $binn->serialize([123, -456, 789]); - $this->assertEquals("\xE0\x0B\x03\x20\x7B\x41\xFE\x38\x40\x03\x15", $binnString); + Assert::assertEquals("\xE0\x0B\x03\x20\x7B\x41\xFE\x38\x40\x03\x15", $binnString); $arrayWithFloat = [458, 5.2349, 94.005000000000000058]; $binnString = $binn->serialize($arrayWithFloat); $binnArray = $binn->unserialize($binnString); - $this->assertEquals($arrayWithFloat, $binnArray); + Assert::assertEquals($arrayWithFloat, $binnArray); $binn2 = new BinnList(); $binn2->addUint8(512)->addInt16(-521); - $this->assertEquals("\xE0\x08\x02\x20\x00\x41\xFD\xF7", $binn2->serialize()); + Assert::assertEquals("\xE0\x09\x02\x40\x02\x00\x41\xFD\xF7", $binn2->serialize()); } public function testSerializeBigSize() { $array = []; for ($i = 0; $i < 512; $i++) { - $array[] = rand(BinnList::INT64_MIN, BinnList::INT64_MAX); + $array[] = random_int(BinnList::INT64_MIN, BinnList::INT64_MAX); } $binn1 = new BinnList; @@ -165,7 +167,7 @@ public function testSerializeBigSize() $binn2->binnOpen($serialized); $unserialized = $binn2->unserialize(); - $this->assertEquals($array, $unserialized); + Assert::assertEquals($array, $unserialized); } public function testSerializeList() @@ -173,31 +175,14 @@ public function testSerializeList() $binn = new BinnList(); $binnString = $binn->serialize(['Hello', ['World']]); - $this->assertEquals("\xE0\x16\x02\xA0\x05Hello\x00\xE0\x0B\x01\xA0\x05World\x00", $binnString); + Assert::assertEquals("\xE0\x16\x02\xA0\x05Hello\x00\xE0\x0B\x01\xA0\x05World\x00", $binnString); } - /** - * @expectedException Knik\Binn\Exceptions\InvalidArrayException - */ - public function testSerializeInvalid() - { - $binn = new BinnList(); - $binn->serialize(['Hello', 'assoc_key' => 'World']); - } - - /** - * @expectedException \Exception - */ public function testInvalidMethod() { + $this->expectException(\Exception::class); + $binn = new BinnList(); $binn->addUnknown('azaza'); } - - public function testValidArray() - { - $this->assertTrue(BinnList::validArray([0, 1, 2])); - $this->assertFalse(BinnList::validArray([1 => 0, 2 => 2])); - $this->assertFalse(BinnList::validArray(['key' => 'val'])); - } -} \ No newline at end of file +} diff --git a/tests/BinnMapTest.php b/tests/Unit/BinnMapTest.php similarity index 71% rename from tests/BinnMapTest.php rename to tests/Unit/BinnMapTest.php index 9f5be2e..aed0714 100644 --- a/tests/BinnMapTest.php +++ b/tests/Unit/BinnMapTest.php @@ -1,11 +1,11 @@ - */ class BinnMapTest extends TestCase { public function testMapList() @@ -32,10 +32,11 @@ public function testMapList() $binnString = "\xE1\x1A\x02\x00\x00\x00\x01\xA0\x03add\x00\x00\x00\x00\x02\xE0\x09\x02\x41\xCF\xC7\x40\x1A\x85"; - $binn = new BinnMap($binnString); + $binn = new BinnMap(); + $binn->binnOpen($binnString); $arr = $binn->getBinnArr(); - $this->assertEquals([1 => 'add', 2 => [-12345, 6789]], $arr); - $this->assertEquals($binnString, $binn->serialize()); + Assert::assertEquals([1 => 'add', 2 => [-12345, 6789]], $arr); + Assert::assertEquals($binnString, $binn->serialize()); } public function testMapOpen() @@ -43,14 +44,8 @@ public function testMapOpen() $binnString = "\xE1\x1A\x02\x00\x00\x00\x01\xA0\x03add\x00\x00\x00\x00\x02\xE0\x09\x02\x41\xCF\xC7\x40\x1A\x85"; $binn = new BinnMap(); $binn->binnOpen($binnString); - $this->assertEquals([1 => 'add', 2 => [-12345, 6789]], $binn->unserialize()); - } - public function testValidArray() - { - $this->assertFalse(BinnMap::validArray([0, 1, 2])); - $this->assertTrue(BinnMap::validArray([1 => 0, 2 => 2])); - $this->assertFalse(BinnMap::validArray(['key' => 'val'])); + Assert::assertEquals([1 => 'add', 2 => [-12345, 6789]], $binn->unserialize()); } public function testNegativeKey() @@ -58,6 +53,9 @@ public function testNegativeKey() $binn = new BinnMap(); $array = [-2 => 543, -8 => 'test']; $binnString = $binn->serialize($array); - $this->assertEquals($array, $binn->unserialize($binnString)); + + $result = $binn->unserialize($binnString); + + Assert::assertEquals($array, $result); } -} \ No newline at end of file +} diff --git a/tests/BinnObjectTest.php b/tests/Unit/BinnObjectTest.php similarity index 75% rename from tests/BinnObjectTest.php rename to tests/Unit/BinnObjectTest.php index 61ba7a9..a1d33df 100644 --- a/tests/BinnObjectTest.php +++ b/tests/Unit/BinnObjectTest.php @@ -1,12 +1,12 @@ - */ class BinnObjectTest extends TestCase { public function testObject() @@ -27,10 +27,11 @@ public function testObject() $binnString = "\xE2\x11\x01\x05hello\xA0\x05world\x00"; - $binn = new BinnObject($binnString); + $binn = new BinnObject(); + $binn->binnOpen($binnString); $arr = $binn->unserialize(); - $this->assertEquals(['hello' => 'world'], $arr); - $this->assertEquals($binnString, $binn->serialize()); + Assert::assertEquals(['hello' => 'world'], $arr); + Assert::assertEquals($binnString, $binn->serialize()); } public function testListObjects() @@ -75,14 +76,7 @@ public function testListObjects() $binn = new Binn(); $arr = $binn->unserialize($binnString); - $this->assertEquals([ ["id" => 1, "name" => "John"], ["id" => 2, "name" => "Eric"] ], $arr); - } - - public function testValidArray() - { - $this->assertFalse(BinnObject::validArray([0, 1, 2])); - $this->assertFalse(BinnObject::validArray([1 => 0, 2 => 2])); - $this->assertTrue(BinnObject::validArray(['key' => 'val'])); + Assert::assertEquals([ ["id" => 1, "name" => "John"], ["id" => 2, "name" => "Eric"] ], $arr); } public function testObjectOpen() @@ -91,7 +85,7 @@ public function testObjectOpen() $binn = new BinnObject(); $binn->binnOpen($binnString); - $this->assertEquals(['hello' => 'world'], $binn->unserialize()); + Assert::assertEquals(['hello' => 'world'], $binn->unserialize()); } public function testSerialize() @@ -100,16 +94,7 @@ public function testSerialize() $binn = new BinnObject(); $serialized = $binn->serialize($array); - $this->assertEquals("\xE2\x11\x01\x05hello\xA0\x05world\x00", $serialized); - } - - /** - * @expectedException Knik\Binn\Exceptions\InvalidArrayException - */ - public function testSerializeInvalid() - { - $binn = new BinnObject(); - $binn->serialize(['list', 'array']); + Assert::assertEquals("\xE2\x11\x01\x05hello\xA0\x05world\x00", $serialized); } public function testSerializeContainers() @@ -120,6 +105,6 @@ public function testSerializeContainers() $serialized = $binn->serialize($array); $unserizlized = $binn->unserialize($serialized); - $this->assertEquals($array, $unserizlized); + Assert::assertEquals($array, $unserizlized); } -} \ No newline at end of file +} diff --git a/tests/BinnTest.php b/tests/Unit/BinnTest.php similarity index 66% rename from tests/BinnTest.php rename to tests/Unit/BinnTest.php index fbab5d1..f1d9cff 100644 --- a/tests/BinnTest.php +++ b/tests/Unit/BinnTest.php @@ -1,14 +1,14 @@ - */ class BinnTest extends TestCase { public function testSerialize() @@ -17,15 +17,15 @@ public function testSerialize() // List $binnString = $binn->serialize([123, -456, 789]); - $this->assertEquals("\xE0\x0B\x03\x20\x7B\x41\xFE\x38\x40\x03\x15", $binnString); + Assert::assertEquals("\xE0\x0B\x03\x20\x7B\x41\xFE\x38\x40\x03\x15", $binnString); // Map $binnString = $binn->serialize([1 => 'add', 2 => [-12345, 6789]]); - $this->assertEquals("\xE1\x1A\x02\x00\x00\x00\x01\xA0\x03add\x00\x00\x00\x00\x02\xE0\x09\x02\x41\xCF\xC7\x40\x1A\x85", $binnString); + Assert::assertEquals("\xE1\x1A\x02\x00\x00\x00\x01\xA0\x03add\x00\x00\x00\x00\x02\xE0\x09\x02\x41\xCF\xC7\x40\x1A\x85", $binnString); // Object $binnString = $binn->serialize(['hello' => 'world']); - $this->assertEquals("\xE2\x11\x01\x05hello\xA0\x05world\x00", $binnString); + Assert::assertEquals("\xE2\x11\x01\x05hello\xA0\x05world\x00", $binnString); // Null $binnString = $binn->serialize(null); @@ -37,30 +37,30 @@ public function testUnserialize() // List $array = $binn->unserialize("\xE0\x0B\x03\x20\x7B\x41\xFE\x38\x40\x03\x15"); - $this->assertEquals([123, -456, 789], $array); + Assert::assertEquals([123, -456, 789], $array); // Map $array = $binn->unserialize("\xE1\x1A\x02\x00\x00\x00\x01\xA0\x03add\x00\x00\x00\x00\x02\xE0\x09\x02\x41\xCF\xC7\x40\x1A\x85"); - $this->assertEquals([1 => 'add', 2 => [-12345, 6789]], $array); + Assert::assertEquals([1 => 'add', 2 => [-12345, 6789]], $array); // Object $binnString = $binn->unserialize("\xE2\x11\x01\x05hello\xA0\x05world\x00"); - $this->assertEquals(['hello' => 'world'], $binnString); + Assert::assertEquals(['hello' => 'world'], $binnString); // Empty $binn = new Binn; - $this->assertCount(0, $binn->unserialize()); + Assert::assertEmpty($binn->unserialize()); // String $binn = new Binn; - $this->assertEquals(null, $binn->unserialize("\xA0\x05Hello\x00")); + Assert::assertEquals('Hello', $binn->unserialize("\xA0\x05Hello\x00")); } public function testSerializeUnserializeBigCount() { $array = []; for ($i = 0; $i < 512; $i++) { - $array[] = rand(-256, 256); + $array[] = random_int(-256, 256); } $array[] = implode('', $array); @@ -71,7 +71,7 @@ public function testSerializeUnserializeBigCount() $binn2 = new Binn; $unserialized = $binn2->unserialize($serialized); - $this->assertEquals($array, $unserialized); + Assert::assertEquals($array, $unserialized); } public function testSerializeTypes() @@ -82,20 +82,11 @@ public function testSerializeTypes() 28 => 2.3, 32 => -2.3, 55 => 45.0034525, 56 => -45.0034525, 57 => null]; $serialized = $binn1->serialize($array); - file_put_contents('test.bin', $serialized); $binn2 = new Binn; $unserialized = $binn2->unserialize($serialized); - $this->assertEquals($array, $unserialized, '', 0.000001); - } - - public function testSerializeNullContainers() - { - $binn = new Binn; - $binn->setContainersClasses([]); - $serialized = $binn->serialize(['array']); - $this->assertNull($serialized); + Assert::assertEqualsWithDelta($array, $unserialized, 0.000001); } public function testObject() @@ -107,6 +98,6 @@ public function testObject() $arraySerialized = $binn1->serialize($array); $objectSerialized = $binn1->serialize($object); - $this->assertEquals($arraySerialized, $objectSerialized); + Assert::assertEquals($arraySerialized, $objectSerialized); } -} \ No newline at end of file +} diff --git a/tests/Unit/Decoder/BinnContainersDecodeTest.php b/tests/Unit/Decoder/BinnContainersDecodeTest.php new file mode 100644 index 0000000..4a4a3d3 --- /dev/null +++ b/tests/Unit/Decoder/BinnContainersDecodeTest.php @@ -0,0 +1,37 @@ +getDecoder(); + + $result = $decoder->decode($bytes, 'binn'); + + Assert::assertEquals($expected, $result); + } + + public function valuesDataProvider() + { + yield [ + "\xE0\x0B\x03\x20\x7B\x41\xFE\x38\x40\x03\x15", + [123, -456, 789] + ]; + + yield [ + "\xE1\x1A\x02\x00\x00\x00\x01\xA0\x03add\x00\x00\x00\x00\x02\xE0\x09\x02\x41\xCF\xC7\x40\x1A\x85", + [1 => 'add', 2 => [-12345, 6789]] + ]; + + yield [ + "\xE2\x11\x01\x05hello\xA0\x05world\x00", + ['hello' => 'world'] + ]; + } +} diff --git a/tests/Unit/Decoder/BinnDecodeTest.php b/tests/Unit/Decoder/BinnDecodeTest.php new file mode 100644 index 0000000..b3f13a3 --- /dev/null +++ b/tests/Unit/Decoder/BinnDecodeTest.php @@ -0,0 +1,40 @@ +getDecoder(); + + $result = $decoder->decode($bytes, 'binn'); + + Assert::assertEqualsWithDelta($expected, $result, 0.00001); + } + + public function testDecodeLongSize(): void + { + $decoder = $this->getDecoder(); + + $result = $decoder->decode("\xA0\x80\x00\x00\x05\x68\x65\x6C\x6C\x6F\x00", 'binn'); + + Assert::assertEquals('hello', $result); + } + + public function testBlobDecode() + { + $decoder = $this->getDecoder(); + + $result = $decoder->decode("\xC0\x80\x00\x00\x05\xFF\xFF\xFF\xFF\xFF", 'binn'); + + Assert::assertEquals("\xFF\xFF\xFF\xFF\xFF", $result); + } +} diff --git a/tests/Unit/Encoder/BinnEncodeContainersTest.php b/tests/Unit/Encoder/BinnEncodeContainersTest.php new file mode 100644 index 0000000..f774058 --- /dev/null +++ b/tests/Unit/Encoder/BinnEncodeContainersTest.php @@ -0,0 +1,115 @@ +getEncoder(); + + $result = $encoder->encode([123, -456, 789], 'binn'); + + Assert::assertEquals("\xE0\x0B\x03\x20\x7B\x41\xFE\x38\x40\x03\x15", $result); + } + + public function testStringsListEncode(): void + { + $encoder = $this->getEncoder(); + + $result = $encoder->encode(['Hello', " World!"], 'binn'); + + Assert::assertEquals( + "\xE0" + . "\x15" + . "\x02" + . "\xA0" + . "\x05" + . "Hello\x00" + . "\xA0" + . "\x07" + . " World!\x00", + $result + ); + } + + // https://github.com/liteserver/binn/blob/master/spec.md#a-list-inside-a-map + public function testListInsideMapEncode(): void + { + $encoder = $this->getEncoder(); + + $result = $encoder->encode([ + 1 => 'add', + 2 => [-12345, 6789] + ], 'binn'); + + Assert::assertEquals( + "\xE1" // [type] map (container) + . "\x1A" // [size] container total size + . "\x02" // [count] key/value pairs + + . "\x00\x00\x00\x01" // key + . "\xA0" // [type] = string + . "\x03" // [size] + . "add\x00" // [data] (null terminated) + + . "\x00\x00\x00\x02" // key + . "\xE0" // [type] list (container) + . "\x09" // [size] container total size + . "\x02" // [count] items + . "\x41" // [type] = int16 + . "\xCF\xC7" // [data] (-12345) + . "\x40" // [type] = uint16 + . "\x1A\x85", // [data] (6789) + $result + ); + } + + // https://github.com/liteserver/binn/blob/master/spec.md#a-list-of-objects + public function testListObjects(): void + { + $encoder = $this->getEncoder(); + + $result = $encoder->encode([ + ["id" => 1, "name" => "John"], + ["id" => 2, "name" => "Eric"] + ], 'binn'); + + Assert::assertEquals( + "\xE0" // [type] list (container) + . "\x2B" // [size] container total size + . "\x02" // [count] items + + . "\xE2" // [type] object (container) + . "\x14" // [size] container total size + . "\x02" // [count] key/value pairs + + . "\x02id" // key + . "\x20" // [type] = uint8 + . "\x01" // [data] (1) + + . "\x04name" // key + . "\xA0" // [type] = string + . "\x04" // [size] + . "John\x00" // [data] (null terminated) + + . "\xE2" // [type] object (container) + . "\x14" // [size] container total size + . "\x02" // [count] key/value pairs + + . "\x02id" // key + . "\x20" // [type] = uint8 + . "\x02" // [data] (2) + + . "\x04name" // key + . "\xA0" // [type] = string + . "\x04Eric" // [size] + . "\x00", // [data] (null terminated) + $result + ); + } +} diff --git a/tests/Unit/Encoder/BinnEncodeTest.php b/tests/Unit/Encoder/BinnEncodeTest.php new file mode 100644 index 0000000..ac4bdd4 --- /dev/null +++ b/tests/Unit/Encoder/BinnEncodeTest.php @@ -0,0 +1,68 @@ +getEncoder(); + + $result = $encoder->encode($value, 'binn'); + + Assert::assertEquals($expectedBytes, $result); + } + + public function testSimpleTypeEncode(): void + { + $encoder = $this->getEncoder(); + + $result = $encoder->encode(7, 'binn'); + + Assert::assertEquals("\x20\x07", $result); + } + + public function testInt16Encode(): void + { + $encoder = $this->getEncoder(); + + $result = $encoder->encode(513, 'binn'); + + Assert::assertEquals("\x40\x02\x01", $result); + } + + public function testInt32Encode(): void + { + $encoder = $this->getEncoder(); + + $result = $encoder->encode(100000, 'binn'); + + Assert::assertEquals("\x60\x00\x01\x86\xA0", $result); + } + + public function testInt64Encode(): void + { + $encoder = $this->getEncoder(); + + $result = $encoder->encode(9223372036854775807, 'binn'); + + Assert::assertEquals("\x80\x7F\xFF\xFF\xFF\xFF\xFF\xFF\xFF", $result); + } + + public function testBigListEncode(): void + { + $encoder = $this->getEncoder(); + + $result = $encoder->encode([str_repeat('string', 200)], 'binn'); + + Assert::assertEquals("\xE0\x80\x00\x04\xBC\x01\xA0\x80\x00\x04\xB0" + . str_repeat('string', 200) + . "\x00", + $result + ); + } +} diff --git a/tests/Unit/Encoder/BinnEncoderTest.php b/tests/Unit/Encoder/BinnEncoderTest.php new file mode 100644 index 0000000..f3c208b --- /dev/null +++ b/tests/Unit/Encoder/BinnEncoderTest.php @@ -0,0 +1,28 @@ +encode(['test' => 'test'], 'binn'); + + Assert::assertEquals("\xE2\x0F\x01\x04\x74\x65\x73\x74\xA0\x04\x74\x65\x73\x74\x00", $result); + } + + public function testDecode() + { + $encoder = new BinnEncoder(); + + $result = $encoder->decode("\xE2\x0F\x01\x04\x74\x65\x73\x74\xA0\x04\x74\x65\x73\x74\x00", 'binn'); + + Assert::assertEquals(['test' => 'test'], $result); + } +} diff --git a/tests/Unit/Encoder/BlobEncodeTest.php b/tests/Unit/Encoder/BlobEncodeTest.php new file mode 100644 index 0000000..de3a966 --- /dev/null +++ b/tests/Unit/Encoder/BlobEncodeTest.php @@ -0,0 +1,21 @@ +getEncoder(); + $file = fopen('tests/Files/file.jpg', 'rb'); + + $result = $encoder->encode($file, 'binn'); + + Assert::assertEquals("\xC0\x80\x00\x61\x24", substr($result, 0, 5)); + Assert::assertStringEqualsFile('tests/Files/file.jpg', substr($result, 5)); + } + +}