@@ -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