44
55use Bolt \PackStream \IPacker ;
66use Bolt \error \PackException ;
7- use Generator ;
7+ use Bolt \ PackStream \{ IPackListGenerator , IPackDictionaryGenerator } ;
88use Bolt \structures \{
99 IStructure ,
1010 Date ,
@@ -51,78 +51,86 @@ class Packer implements IPacker
5151 * Pack message with parameters
5252 * @param $signature
5353 * @param mixed ...$params
54- * @return Generator
54+ * @return iterable
5555 * @throws PackException
5656 */
57- public function pack ($ signature , ...$ params ): Generator
57+ public function pack ($ signature , ...$ params ): iterable
5858 {
59- $ output = '' ;
60-
6159 $ this ->littleEndian = unpack ('S ' , "\x01\x00" )[1 ] === 1 ;
6260
6361 //structure
6462 $ length = count ($ params );
6563 if ($ length < self ::SMALL ) { //TINY_STRUCT
66- $ output .= pack ('C ' , 0b10110000 | $ length );
64+ yield pack ( ' n ' , 1 ) . pack ('C ' , 0b10110000 | $ length );
6765 } elseif ($ length < self ::MEDIUM ) { //STRUCT_8
68- $ output .= chr (0xDC ) . pack ('C ' , $ length );
66+ yield pack ( ' n ' , 2 ) . chr (0xDC ) . pack ('C ' , $ length );
6967 } elseif ($ length < self ::LARGE ) { //STRUCT_16
70- $ output .= chr (0xDD ) . pack ('n ' , $ length );
68+ yield pack ( ' n ' , 4 ) . chr (0xDD ) . pack ('n ' , $ length );
7169 } else {
7270 throw new PackException ('Too many parameters ' );
7371 }
7472
75- $ output .= chr ($ signature );
73+ yield pack ( ' n ' , 1 ) . chr ($ signature );
7674
7775 foreach ($ params as $ param ) {
78- $ output .= $ this ->p ($ param );
79- }
80-
81- //structure buffer
82- $ totalLength = mb_strlen ($ output , '8bit ' );
83- $ offset = 0 ;
84- while ($ offset < $ totalLength ) {
85- $ chunk = mb_strcut ($ output , $ offset , 65535 , '8bit ' );
86- $ chunkLength = mb_strlen ($ chunk , '8bit ' );
87- $ offset += $ chunkLength ;
88- yield pack ('n ' , $ chunkLength ) . $ chunk ;
76+ foreach ($ this ->p ($ param ) as $ packed ) {
77+ $ totalLength = mb_strlen ($ packed , '8bit ' );
78+ $ offset = 0 ;
79+ while ($ offset < $ totalLength ) {
80+ $ chunk = mb_strcut ($ packed , $ offset , 65535 , '8bit ' );
81+ $ chunkLength = mb_strlen ($ chunk , '8bit ' );
82+ $ offset += $ chunkLength ;
83+ yield pack ('n ' , $ chunkLength ) . $ chunk ;
84+ }
85+ }
8986 }
9087
9188 yield chr (0x00 ) . chr (0x00 );
9289 }
9390
9491 /**
9592 * @param mixed $param
96- * @return string
93+ * @return iterable
9794 * @throws PackException
9895 */
99- private function p ($ param ): string
96+ private function p ($ param ): iterable
10097 {
10198 switch (gettype ($ param )) {
10299 case 'integer ' :
103- return $ this ->packInteger ($ param );
100+ yield from $ this ->packInteger ($ param );
101+ break ;
104102 case 'double ' :
105- return $ this ->packFloat ($ param );
103+ yield from $ this ->packFloat ($ param );
104+ break ;
106105 case 'boolean ' :
107- return chr ($ param ? 0xC3 : 0xC2 );
106+ yield chr ($ param ? 0xC3 : 0xC2 );
107+ break ;
108108 case 'NULL ' :
109- return chr (0xC0 );
109+ yield chr (0xC0 );
110+ break ;
110111 case 'string ' :
111- return $ this ->packString ($ param );
112+ yield from $ this ->packString ($ param );
113+ break ;
112114 case 'array ' :
113115 if ($ param === array_values ($ param )) {
114- return $ this ->packList ($ param );
116+ yield from $ this ->packList ($ param );
115117 } else {
116- return $ this ->packMap ($ param );
118+ yield from $ this ->packDictionary ($ param );
117119 }
120+ break ;
118121 case 'object ' :
119122 if ($ param instanceof IStructure) {
120- return $ this ->packStructure ($ param );
123+ yield from $ this ->packStructure ($ param );
121124 } elseif ($ param instanceof Bytes) {
122- return $ this ->packByteArray ($ param );
125+ yield from $ this ->packByteArray ($ param );
126+ } elseif ($ param instanceof IPackListGenerator) {
127+ yield from $ this ->packList ($ param );
128+ } elseif ($ param instanceof IPackDictionaryGenerator) {
129+ yield from $ this ->packDictionary ($ param );
123130 } else {
124- return $ this ->packMap ((array )$ param );
131+ yield from $ this ->packDictionary ((array )$ param );
125132 }
133+ break ;
126134
127135 default :
128136 throw new PackException ('Not recognized type of parameter ' );
@@ -131,155 +139,147 @@ private function p($param): string
131139
132140 /**
133141 * @param string $str
134- * @return string
142+ * @return iterable
135143 * @throws PackException
136144 */
137- private function packString (string $ str ): string
145+ private function packString (string $ str ): iterable
138146 {
139147 $ length = mb_strlen ($ str , '8bit ' );
140148
141149 if ($ length < self ::SMALL ) { //TINY_STRING
142- return pack ('C ' , 0b10000000 | $ length ) . $ str ;
150+ yield pack ('C ' , 0b10000000 | $ length ) . $ str ;
143151 } elseif ($ length < self ::MEDIUM ) { //STRING_8
144- return chr (0xD0 ) . pack ('C ' , $ length ) . $ str ;
152+ yield chr (0xD0 ) . pack ('C ' , $ length ) . $ str ;
145153 } elseif ($ length < self ::LARGE ) { //STRING_16
146- return chr (0xD1 ) . pack ('n ' , $ length ) . $ str ;
154+ yield chr (0xD1 ) . pack ('n ' , $ length ) . $ str ;
147155 } elseif ($ length < self ::HUGE ) { //STRING_32
148- return chr (0xD2 ) . pack ('N ' , $ length ) . $ str ;
156+ yield chr (0xD2 ) . pack ('N ' , $ length ) . $ str ;
149157 } else {
150158 throw new PackException ('String too long ' );
151159 }
152160 }
153161
154162 /**
155163 * @param float $value
156- * @return string
164+ * @return iterable
157165 */
158- private function packFloat (float $ value ): string
166+ private function packFloat (float $ value ): iterable
159167 {
160168 $ packed = pack ('d ' , $ value );
161- return chr (0xC1 ) . ($ this ->littleEndian ? strrev ($ packed ) : $ packed );
169+ yield chr (0xC1 ) . ($ this ->littleEndian ? strrev ($ packed ) : $ packed );
162170 }
163171
164172 /**
165173 * @param int $value
166- * @return string
174+ * @return iterable
167175 * @throws PackException
168176 */
169- private function packInteger (int $ value ): string
177+ private function packInteger (int $ value ): iterable
170178 {
171179 if ($ value >= -16 && $ value <= 127 ) { //TINY_INT
172- return pack ('c ' , $ value );
180+ yield pack ('c ' , $ value );
173181 } elseif ($ value >= -128 && $ value <= -17 ) { //INT_8
174- return chr (0xC8 ) . pack ('c ' , $ value );
182+ yield chr (0xC8 ) . pack ('c ' , $ value );
175183 } elseif (($ value >= 128 && $ value <= 32767 ) || ($ value >= -32768 && $ value <= -129 )) { //INT_16
176184 $ packed = pack ('s ' , $ value );
177- return chr (0xC9 ) . ($ this ->littleEndian ? strrev ($ packed ) : $ packed );
185+ yield chr (0xC9 ) . ($ this ->littleEndian ? strrev ($ packed ) : $ packed );
178186 } elseif (($ value >= 32768 && $ value <= 2147483647 ) || ($ value >= -2147483648 && $ value <= -32769 )) { //INT_32
179187 $ packed = pack ('l ' , $ value );
180- return chr (0xCA ) . ($ this ->littleEndian ? strrev ($ packed ) : $ packed );
188+ yield chr (0xCA ) . ($ this ->littleEndian ? strrev ($ packed ) : $ packed );
181189 } elseif (($ value >= 2147483648 && $ value <= 9223372036854775807 ) || ($ value >= -9223372036854775808 && $ value <= -2147483649 )) { //INT_64
182190 $ packed = pack ('q ' , $ value );
183- return chr (0xCB ) . ($ this ->littleEndian ? strrev ($ packed ) : $ packed );
191+ yield chr (0xCB ) . ($ this ->littleEndian ? strrev ($ packed ) : $ packed );
184192 } else {
185193 throw new PackException ('Integer out of range ' );
186194 }
187195 }
188196
189197 /**
190- * @param array $arr
191- * @return string
198+ * @param array|IPackDictionaryGenerator $param
199+ * @return iterable
192200 * @throws PackException
193201 */
194- private function packMap ( array $ arr ): string
202+ private function packDictionary ( $ param ): iterable
195203 {
196- $ output = '' ;
197- $ size = count ($ arr );
204+ $ size = is_array ($ param ) ? count ($ param ) : $ param ->count ();
198205
199206 if ($ size < self ::SMALL ) { //TINY_MAP
200- $ output .= pack ('C ' , 0b10100000 | $ size );
207+ yield pack ('C ' , 0b10100000 | $ size );
201208 } elseif ($ size < self ::MEDIUM ) { //MAP_8
202- $ output .= chr (0xD8 ) . pack ('C ' , $ size );
209+ yield chr (0xD8 ) . pack ('C ' , $ size );
203210 } elseif ($ size < self ::LARGE ) { //MAP_16
204- $ output .= chr (0xD9 ) . pack ('n ' , $ size );
211+ yield chr (0xD9 ) . pack ('n ' , $ size );
205212 } elseif ($ size < self ::HUGE ) { //MAP_32
206- $ output .= chr (0xDA ) . pack ('N ' , $ size );
213+ yield chr (0xDA ) . pack ('N ' , $ size );
207214 } else {
208215 throw new PackException ('Too many map elements ' );
209216 }
210217
211- foreach ($ arr as $ k => $ v ) {
212- $ output .= $ this ->p ((string )$ k ); // The key names in a map must be of type String.
213- $ output .= $ this ->p ($ v );
218+ foreach ($ param as $ k => $ v ) {
219+ yield from $ this ->p ((string )$ k ); // The key names in a map must be of type String.
220+ yield from $ this ->p ($ v );
214221 }
215-
216- return $ output ;
217222 }
218223
219224 /**
220- * @param array $arr
221- * @return string
225+ * @param array|IPackListGenerator $param
226+ * @return iterable
222227 * @throws PackException
223228 */
224- private function packList (array $ arr ): string
229+ private function packList ($ param ): iterable
225230 {
226- $ output = '' ;
227- $ size = count ($ arr );
231+ $ size = is_array ($ param ) ? count ($ param ) : $ param ->count ();
228232
229233 if ($ size < self ::SMALL ) { //TINY_LIST
230- $ output .= pack ('C ' , 0b10010000 | $ size );
234+ yield pack ('C ' , 0b10010000 | $ size );
231235 } elseif ($ size < self ::MEDIUM ) { //LIST_8
232- $ output .= chr (0xD4 ) . pack ('C ' , $ size );
236+ yield chr (0xD4 ) . pack ('C ' , $ size );
233237 } elseif ($ size < self ::LARGE ) { //LIST_16
234- $ output .= chr (0xD5 ) . pack ('n ' , $ size );
238+ yield chr (0xD5 ) . pack ('n ' , $ size );
235239 } elseif ($ size < self ::HUGE ) { //LIST_32
236- $ output .= chr (0xD6 ) . pack ('N ' , $ size );
240+ yield chr (0xD6 ) . pack ('N ' , $ size );
237241 } else {
238242 throw new PackException ('Too many list elements ' );
239243 }
240244
241- foreach ($ arr as $ v ) {
242- $ output .= $ this ->p ($ v );
245+ foreach ($ param as $ v ) {
246+ yield from $ this ->p ($ v );
243247 }
244-
245- return $ output ;
246248 }
247249
248250 /**
249251 * @param IStructure $structure
250- * @return string
252+ * @return iterable
251253 * @throws PackException
252254 */
253- private function packStructure (IStructure $ structure ): string
255+ private function packStructure (IStructure $ structure ): iterable
254256 {
255257 $ arr = $ this ->structuresLt [get_class ($ structure )] ?? null ;
256258 if ($ arr === null ) {
257259 throw new PackException ('Provided structure as parameter is not supported ' );
258260 }
259261
260262 $ signature = chr (array_shift ($ arr ));
261- $ output = pack ('C ' , 0b10110000 | count ($ arr )) . $ signature ;
263+ yield pack ('C ' , 0b10110000 | count ($ arr )) . $ signature ;
262264 foreach ($ arr as $ structureMethod => $ packerMethod ) {
263- $ output .= $ this ->{$ packerMethod }($ structure ->{$ structureMethod }());
265+ yield from $ this ->{$ packerMethod }($ structure ->{$ structureMethod }());
264266 }
265-
266- return $ output ;
267267 }
268268
269269 /**
270270 * @param Bytes $bytes
271- * @return string
271+ * @return iterable
272272 * @throws PackException
273273 */
274- private function packByteArray (Bytes $ bytes ): string
274+ private function packByteArray (Bytes $ bytes ): iterable
275275 {
276276 $ size = count ($ bytes );
277277 if ($ size < self ::MEDIUM ) {
278- return chr (0xCC ) . pack ('C ' , $ size ) . $ bytes ;
278+ yield chr (0xCC ) . pack ('C ' , $ size ) . $ bytes ;
279279 } elseif ($ size < self ::LARGE ) {
280- return chr (0xCD ) . pack ('n ' , $ size ) . $ bytes ;
280+ yield chr (0xCD ) . pack ('n ' , $ size ) . $ bytes ;
281281 } elseif ($ size <= 2147483647 ) {
282- return chr (0xCE ) . pack ('N ' , $ size ) . $ bytes ;
282+ yield chr (0xCE ) . pack ('N ' , $ size ) . $ bytes ;
283283 } else {
284284 throw new PackException ('ByteArray too big ' );
285285 }
0 commit comments