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