Skip to content

Commit 5949bc9

Browse files
Merge pull request #81 from neo4j-php/optimize
optimizations
2 parents a24d951 + 0c3fd01 commit 5949bc9

File tree

6 files changed

+216
-232
lines changed

6 files changed

+216
-232
lines changed

src/PackStream/IUnpacker.php

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,15 @@
1212
interface IUnpacker
1313
{
1414
/**
15+
* Unpack message
1516
* @param string $msg
16-
* @param int $signature
1717
* @return mixed
1818
*/
19-
public function unpack(string $msg, int &$signature);
19+
public function unpack(string $msg);
20+
21+
/**
22+
* Get unpacked message status signature
23+
* @return int
24+
*/
25+
public function getSignature(): int;
2026
}

src/PackStream/v1/Packer.php

Lines changed: 52 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,11 @@ class Packer implements IPacker
3333
private const LARGE = 65536;
3434
private const HUGE = 4294967295;
3535

36+
/**
37+
* @var bool
38+
*/
39+
private $littleEndian;
40+
3641
private $structuresLt = [
3742
Relationship::class => [0x52, 'id' => 'packInteger', 'startNodeId' => 'packInteger', 'endNodeId' => 'packInteger', 'type' => 'packString', 'properties' => 'packMap'],
3843
Date::class => [0x44, 'days' => 'packInteger'],
@@ -57,6 +62,8 @@ public function pack($signature, ...$params): Generator
5762
{
5863
$output = '';
5964

65+
$this->littleEndian = unpack('S', "\x01\x00")[1] === 1;
66+
6067
//structure
6168
$length = count($params);
6269
if ($length < self::SMALL) { //TINY_STRUCT
@@ -76,10 +83,13 @@ public function pack($signature, ...$params): Generator
7683
}
7784

7885
//structure buffer
79-
$len = mb_strlen($output, '8bit');
80-
for ($i = 0; $i < $len; $i += 65535) {
81-
$chunk = mb_substr($output, $i, 65535, '8bit');
82-
yield pack('n', mb_strlen($chunk, '8bit')) . $chunk;
86+
$totalLength = mb_strlen($output, '8bit');
87+
$offset = 0;
88+
while ($offset < $totalLength) {
89+
$chunk = mb_strcut($output, $offset, 65535, '8bit');
90+
$chunkLength = mb_strlen($chunk, '8bit');
91+
$offset += $chunkLength;
92+
yield pack('n', $chunkLength) . $chunk;
8393
}
8494

8595
yield chr(0x00) . chr(0x00);
@@ -92,33 +102,33 @@ public function pack($signature, ...$params): Generator
92102
*/
93103
private function p($param): string
94104
{
95-
$output = '';
96-
if (is_int($param)) {
97-
$output .= $this->packInteger($param);
98-
} elseif (is_float($param)) {
99-
$output .= $this->packFloat($param);
100-
} elseif (is_null($param)) {
101-
$output .= chr(0xC0);
102-
} elseif (is_bool($param)) {
103-
$output .= chr($param ? 0xC3 : 0xC2);
104-
} elseif (is_string($param)) {
105-
$output .= $this->packString($param);
106-
} elseif ($param instanceof IStructure) {
107-
$output .= $this->packStructure($param);
108-
} elseif (is_array($param)) {
109-
$keys = array_keys($param);
110-
if (count($param) == 0 || count(array_filter($keys, 'is_int')) == count($keys)) {
111-
$output .= $this->packList($param);
112-
} else {
113-
$output .= $this->packMap($param);
114-
}
115-
} elseif (is_object($param)) {
116-
$output .= $this->packMap((array)$param);
117-
} else {
118-
throw new PackException('Not recognized type of parameter');
119-
}
105+
switch (gettype($param)) {
106+
case 'integer':
107+
return $this->packInteger($param);
108+
case 'double':
109+
return $this->packFloat($param);
110+
case 'boolean':
111+
return chr($param ? 0xC3 : 0xC2);
112+
case 'NULL':
113+
return chr(0xC0);
114+
case 'string':
115+
return $this->packString($param);
116+
case 'array':
117+
if ($param === array_values($param)) {
118+
return $this->packList($param);
119+
} else {
120+
return $this->packMap($param);
121+
}
122+
case 'object':
123+
if ($param instanceof IStructure) {
124+
return $this->packStructure($param);
125+
} else {
126+
return $this->packMap((array)$param);
127+
}
120128

121-
return $output;
129+
default:
130+
throw new PackException('Not recognized type of parameter');
131+
}
122132
}
123133

124134
/**
@@ -128,22 +138,19 @@ private function p($param): string
128138
*/
129139
private function packString(string $str): string
130140
{
131-
$output = '';
132141
$length = mb_strlen($str, '8bit');
133142

134143
if ($length < self::SMALL) { //TINY_STRING
135-
$output .= pack('C', 0b10000000 | $length) . $str;
144+
return pack('C', 0b10000000 | $length) . $str;
136145
} elseif ($length < self::MEDIUM) { //STRING_8
137-
$output .= chr(0xD0) . pack('C', $length) . $str;
146+
return chr(0xD0) . pack('C', $length) . $str;
138147
} elseif ($length < self::LARGE) { //STRING_16
139-
$output .= chr(0xD1) . pack('n', $length) . $str;
148+
return chr(0xD1) . pack('n', $length) . $str;
140149
} elseif ($length < self::HUGE) { //STRING_32
141-
$output .= chr(0xD2) . pack('N', $length) . $str;
150+
return chr(0xD2) . pack('N', $length) . $str;
142151
} else {
143152
throw new PackException('String too long');
144153
}
145-
146-
return $output;
147154
}
148155

149156
/**
@@ -162,33 +169,27 @@ private function packFloat(float $value): string
162169
*/
163170
private function packInteger(int $value): string
164171
{
165-
$output = '';
166-
$tmp = unpack('S', "\x01\x00");
167-
$little = $tmp[1] == 1;
168-
169172
if ($value >= 0 && $value <= 127) { //+TINY_INT
170173
$packed = pack('C', 0b00000000 | $value);
171-
$output .= $little ? strrev($packed) : $packed;
174+
return $this->littleEndian ? strrev($packed) : $packed;
172175
} elseif ($value >= -16 && $value < 0) { //-TINY_INT
173176
$packed = pack('c', 0b11110000 | $value);
174-
$output .= $little ? strrev($packed) : $packed;
177+
return $this->littleEndian ? strrev($packed) : $packed;
175178
} elseif ($value >= -128 && $value <= -17) { //INT_8
176179
$packed = pack('c', $value);
177-
$output .= chr(0xC8) . ($little ? strrev($packed) : $packed);
180+
return chr(0xC8) . ($this->littleEndian ? strrev($packed) : $packed);
178181
} elseif (($value >= 128 && $value <= 32767) || ($value >= -32768 && $value <= -129)) { //INT_16
179182
$packed = pack('s', $value);
180-
$output .= chr(0xC9) . ($little ? strrev($packed) : $packed);
183+
return chr(0xC9) . ($this->littleEndian ? strrev($packed) : $packed);
181184
} elseif (($value >= 32768 && $value <= 2147483647) || ($value >= -2147483648 && $value <= -32769)) { //INT_32
182185
$packed = pack('l', $value);
183-
$output .= chr(0xCA) . ($little ? strrev($packed) : $packed);
186+
return chr(0xCA) . ($this->littleEndian ? strrev($packed) : $packed);
184187
} elseif (($value >= 2147483648 && $value <= 9223372036854775807) || ($value >= -9223372036854775808 && $value <= -2147483649)) { //INT_64
185188
$packed = pack('q', $value);
186-
$output .= chr(0xCB) . ($little ? strrev($packed) : $packed);
189+
return chr(0xCB) . ($this->littleEndian ? strrev($packed) : $packed);
187190
} else {
188191
throw new PackException('Integer out of range');
189192
}
190-
191-
return $output;
192193
}
193194

194195
/**
@@ -257,11 +258,11 @@ private function packList(array $arr): string
257258
*/
258259
private function packStructure(IStructure $structure): string
259260
{
260-
if (!array_key_exists(get_class($structure), $this->structuresLt)) {
261+
$arr = $this->structuresLt[get_class($structure)] ?? null;
262+
if ($arr === null) {
261263
throw new PackException('Provided structure as parameter is not supported');
262264
}
263265

264-
$arr = $this->structuresLt[get_class($structure)];
265266
$signature = chr(array_shift($arr));
266267
$output = pack('C', 0b10110000 | count($arr)) . $signature;
267268
foreach ($arr as $structureMethod => $packerMethod) {

0 commit comments

Comments
 (0)