From 9311cb8576081ecc6198e6ba5edc0c1e040142f4 Mon Sep 17 00:00:00 2001 From: et-nik Date: Mon, 8 Mar 2021 21:21:14 +0300 Subject: [PATCH 1/7] Update binn library --- README.md | 54 +- composer.json | 9 +- coverage.xml | 719 ------------------ examples/binn_example.php | 8 +- phpunit.xml | 13 +- src/Binn.php | 103 +-- src/BinnAbstract.php | 544 +------------ src/BinnList.php | 283 +------ src/BinnMap.php | 287 +------ src/BinnObject.php | 295 +------ src/Contracts/BinnValueDecoder.php | 10 + src/Contracts/BinnValueEncoder.php | 10 + src/Contracts/Container.php | 10 + src/Decoder/BinnDecode.php | 28 + src/Decoder/Containers/BinnListDecoder.php | 71 ++ src/Decoder/Containers/BinnMapDecoder.php | 74 ++ src/Decoder/Containers/BinnObjectDecoder.php | 76 ++ src/Decoder/Decoder.php | 83 ++ src/Decoder/DecoderCollection.php | 37 + src/Decoder/DecoderCollectionFactory.php | 38 + src/Decoder/SimpleStorageValueDecoder.php | 120 +++ src/Decoder/Unpacker.php | 87 +++ src/Encoder/BinnEncode.php | 28 + src/Encoder/BinnEncoder.php | 53 ++ src/Encoder/BlobEncode.php | 36 + src/Encoder/Containers/BinnListEncoder.php | 69 ++ src/Encoder/Containers/BinnMapEncoder.php | 76 ++ src/Encoder/Containers/BinnObjectEncoder.php | 57 ++ src/Encoder/EncoderCollection.php | 37 + src/Encoder/EncoderCollectionFactory.php | 40 + src/Encoder/Packer.php | 91 +++ src/Encoder/SimpleTypeValueEncoder.php | 151 ++++ src/Exceptions/BinnException.php | 10 + src/Exceptions/InvalidArrayException.php | 6 +- tests/BinnAbstractTest.php | 149 ---- tests/BinnTestCase.php | 55 ++ tests/Files/file.jpg | Bin 0 -> 24863 bytes tests/Files/file.txt | 1 + tests/{ => Unit}/BinnListTest.php | 99 +-- tests/{ => Unit}/BinnMapTest.php | 28 +- tests/{ => Unit}/BinnObjectTest.php | 39 +- tests/{ => Unit}/BinnTest.php | 41 +- .../Unit/Decoder/BinnContainersDecodeTest.php | 37 + tests/Unit/Decoder/BinnDecodeTest.php | 40 + .../Unit/Encoder/BinnEncodeContainersTest.php | 115 +++ tests/Unit/Encoder/BinnEncodeTest.php | 68 ++ tests/Unit/Encoder/BinnEncoderTest.php | 28 + tests/Unit/Encoder/BlobEncodeTest.php | 21 + 48 files changed, 1879 insertions(+), 2455 deletions(-) delete mode 100644 coverage.xml create mode 100644 src/Contracts/BinnValueDecoder.php create mode 100644 src/Contracts/BinnValueEncoder.php create mode 100644 src/Contracts/Container.php create mode 100644 src/Decoder/BinnDecode.php create mode 100644 src/Decoder/Containers/BinnListDecoder.php create mode 100644 src/Decoder/Containers/BinnMapDecoder.php create mode 100644 src/Decoder/Containers/BinnObjectDecoder.php create mode 100644 src/Decoder/Decoder.php create mode 100644 src/Decoder/DecoderCollection.php create mode 100644 src/Decoder/DecoderCollectionFactory.php create mode 100644 src/Decoder/SimpleStorageValueDecoder.php create mode 100644 src/Decoder/Unpacker.php create mode 100644 src/Encoder/BinnEncode.php create mode 100644 src/Encoder/BinnEncoder.php create mode 100644 src/Encoder/BlobEncode.php create mode 100644 src/Encoder/Containers/BinnListEncoder.php create mode 100644 src/Encoder/Containers/BinnMapEncoder.php create mode 100644 src/Encoder/Containers/BinnObjectEncoder.php create mode 100644 src/Encoder/EncoderCollection.php create mode 100644 src/Encoder/EncoderCollectionFactory.php create mode 100644 src/Encoder/Packer.php create mode 100644 src/Encoder/SimpleTypeValueEncoder.php create mode 100644 src/Exceptions/BinnException.php delete mode 100644 tests/BinnAbstractTest.php create mode 100644 tests/BinnTestCase.php create mode 100644 tests/Files/file.jpg create mode 100644 tests/Files/file.txt rename tests/{ => Unit}/BinnListTest.php (57%) rename tests/{ => Unit}/BinnMapTest.php (71%) rename tests/{ => Unit}/BinnObjectTest.php (75%) rename tests/{ => Unit}/BinnTest.php (66%) create mode 100644 tests/Unit/Decoder/BinnContainersDecodeTest.php create mode 100644 tests/Unit/Decoder/BinnDecodeTest.php create mode 100644 tests/Unit/Encoder/BinnEncodeContainersTest.php create mode 100644 tests/Unit/Encoder/BinnEncodeTest.php create mode 100644 tests/Unit/Encoder/BinnEncoderTest.php create mode 100644 tests/Unit/Encoder/BlobEncodeTest.php diff --git a/README.md b/README.md index afe72bd..46a6466 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ Binn 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..525945a 100644 --- a/composer.json +++ b/composer.json @@ -13,15 +13,16 @@ } ], "require": { - "php": ">=5.6.3" + "php": ">=7.1", + "symfony/serializer": "^5.2" }, "require-dev": { - "mockery/mockery": "0.9.*", - "phpunit/phpunit": "^5.7" + "phpunit/phpunit": "^7" }, "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..cf77522 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -12,7 +12,7 @@ > - tests + tests/Unit @@ -24,13 +24,4 @@ ./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..8ae6b8a --- /dev/null +++ b/src/Encoder/BlobEncode.php @@ -0,0 +1,36 @@ +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 0000000000000000000000000000000000000000..d08c761df0640a7c93e447a1830b84a16e0dc4cd GIT binary patch literal 24863 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 literal 0 HcmV?d00001 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)); + } + +} From 801ea612d2238fce81bd2045c5697bfcadb776ce Mon Sep 17 00:00:00 2001 From: et-nik Date: Mon, 8 Mar 2021 21:33:22 +0300 Subject: [PATCH 2/7] Travis CI update --- .travis.yml | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/.travis.yml b/.travis.yml index ba81726..93d0df9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,20 +1,15 @@ language: php php: + - nightly + - 8.0 - 7.3 - 7.2 - 7.1 - - 7.0 - - 5.6 - - 5.5 - - 5.4 - - hhvm matrix: allow_failures: - - php: 5.5 - - php: 5.4 - - php: hhvm + - nightly before_install: - composer self-update @@ -24,9 +19,4 @@ after_script: - 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 From b5ccf2e423565204e3ee9e34858f373ea5d3f5dd Mon Sep 17 00:00:00 2001 From: et-nik Date: Mon, 8 Mar 2021 21:37:19 +0300 Subject: [PATCH 3/7] Travis CI update --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 93d0df9..cf7ffad 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,7 +9,7 @@ php: matrix: allow_failures: - - nightly + - php: nightly before_install: - composer self-update From 096d86b86012ec6709a565896bd18b6688778cb7 Mon Sep 17 00:00:00 2001 From: et-nik Date: Mon, 8 Mar 2021 21:49:01 +0300 Subject: [PATCH 4/7] Fix file formatting --- .travis.yml | 6 +++--- src/Encoder/BlobEncode.php | 1 - 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index cf7ffad..a84eead 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,17 +6,17 @@ php: - 7.3 - 7.2 - 7.1 - + matrix: allow_failures: - 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: - travis_retry composer update --no-interaction --prefer-source diff --git a/src/Encoder/BlobEncode.php b/src/Encoder/BlobEncode.php index 8ae6b8a..b6fd0ac 100644 --- a/src/Encoder/BlobEncode.php +++ b/src/Encoder/BlobEncode.php @@ -32,5 +32,4 @@ public function supportsEncoding($value): bool { return is_resource($value); } - } From 7d7ad8d4da512bbdd8188ac68a614b517efb7c28 Mon Sep 17 00:00:00 2001 From: et-nik Date: Mon, 8 Mar 2021 22:26:00 +0300 Subject: [PATCH 5/7] Fix travis CI. Update composer packages --- README.md | 2 +- composer.json | 4 ++-- phpunit.xml | 25 +++---------------------- 3 files changed, 6 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 46a6466..23ae87f 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ 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.org/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) diff --git a/composer.json b/composer.json index 525945a..1691365 100644 --- a/composer.json +++ b/composer.json @@ -14,10 +14,10 @@ ], "require": { "php": ">=7.1", - "symfony/serializer": "^5.2" + "symfony/serializer": "^4.4 || ^5.1 || ^5.2" }, "require-dev": { - "phpunit/phpunit": "^7" + "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0" }, "autoload": { "psr-4": { diff --git a/phpunit.xml b/phpunit.xml index cf77522..dbb74c8 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -1,27 +1,8 @@ - + - - tests/Unit + + ./tests/Unit - - - - ./src/ - - - ./examples/ - - From 67233df13458bbe255a3e75070193a689c58dd31 Mon Sep 17 00:00:00 2001 From: et-nik Date: Mon, 8 Mar 2021 22:28:34 +0300 Subject: [PATCH 6/7] Travis CI. Prefer lowest set --- .travis.yml | 8 ++++++-- composer.json | 6 +++--- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index a84eead..93d34bc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,11 @@ php: - 8.0 - 7.3 - 7.2 - - 7.1 + +env: + matrix: + - PREFER_LOWEST="--prefer-lowest" + - PREFER_LOWEST="" matrix: allow_failures: @@ -19,4 +23,4 @@ after_script: - php ocular.phar code-coverage:upload --format=php-clover coverage.xml install: - - travis_retry composer update --no-interaction --prefer-source + - travis_retry composer update --no-interaction --prefer-source $PREFER_LOWEST diff --git a/composer.json b/composer.json index 1691365..9a0fc63 100644 --- a/composer.json +++ b/composer.json @@ -13,11 +13,11 @@ } ], "require": { - "php": ">=7.1", - "symfony/serializer": "^4.4 || ^5.1 || ^5.2" + "php": ">=7.2", + "symfony/serializer": "^5" }, "require-dev": { - "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0" + "phpunit/phpunit": "^8.0 || ^9.0" }, "autoload": { "psr-4": { From 5382357c2625839ee5a6667202dcbb024e525b88 Mon Sep 17 00:00:00 2001 From: et-nik Date: Mon, 8 Mar 2021 22:39:09 +0300 Subject: [PATCH 7/7] Update readme link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 23ae87f..8c0ba20 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ Binn ==== -[![Build Status](https://travis-ci.com/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)