@@ -27,97 +27,100 @@ public function __toString(): string
2727 return json_encode ([(string )$ this ->type_marker , (string )$ this ->data ]);
2828 }
2929
30- private static array $ formats = ['s ' , 'l ' , 'q ' ];
30+ private static array $ endianesFormats = ['s ' , 'l ' , 'q ' ];
3131
3232 /**
3333 * Encode array as vector structure
34+ * This is a helper method to create Vector structure from array of numbers
3435 * @param int[]|float[] $data
36+ * @param TypeMarker|null $type Optional type to force specific data type .. null = auto decide
3537 * @return self
3638 * @throws \InvalidArgumentException
3739 */
38- public static function encode (array $ data ): self
40+ public static function encode (array $ data, ? TypeMarker $ type = null ): self
3941 {
40- if (count ($ data ) === 0 ) {
41- throw new \InvalidArgumentException ('Vector cannot be empty ' );
42- }
43- if (count ($ data ) > 4096 ) {
44- throw new \InvalidArgumentException ('Vector cannot have more than 4096 elements ' );
42+ $ anyFloat = false ;
43+ foreach ($ data as $ entry ) {
44+ if (!is_int ($ entry ) && !is_float ($ entry )) {
45+ throw new \InvalidArgumentException ('Vector can only contain numeric values ' );
46+ }
47+ if (!$ anyFloat && is_float ($ entry )) {
48+ $ anyFloat = true ;
49+ }
4550 }
4651
47- $ anyFloat = in_array (true , array_map ('is_float ' , $ data ));
48- $ minValue = min ($ data );
49- $ maxValue = max ($ data );
50- $ marker = 0 ;
52+ $ minValue = count ($ data ) ? min ($ data ) : 0 ;
53+ $ maxValue = count ($ data ) ? max ($ data ) : 0 ;
5154 $ packFormat = '' ;
5255
5356 if ($ anyFloat ) {
5457 if ($ minValue >= 1.4e-45 && $ maxValue <= 3.4028235e+38 ) { // Single precision float (FLOAT_32)
55- $ marker = 0xC6 ;
58+ if ( $ type === null ) $ type = TypeMarker:: FLOAT_32 ;
5659 $ packFormat = 'G ' ;
5760 } else { // Double precision float (FLOAT_64)
58- $ marker = 0xC1 ;
61+ if ( $ type === null ) $ type = TypeMarker:: FLOAT_64 ;
5962 $ packFormat = 'E ' ;
6063 }
6164 } else {
6265 if ($ minValue >= -128 && $ maxValue <= 127 ) { // INT_8
63- $ marker = 0xC8 ;
66+ if ( $ type === null ) $ type = TypeMarker:: INT_8 ;
6467 $ packFormat = 'c ' ;
6568 } elseif ($ minValue >= -32768 && $ maxValue <= 32767 ) { // INT_16
66- $ marker = 0xC9 ;
69+ if ( $ type === null ) $ type = TypeMarker:: INT_16 ;
6770 $ packFormat = 's ' ;
6871 } elseif ($ minValue >= -2147483648 && $ maxValue <= 2147483647 ) { // INT_32
69- $ marker = 0xCA ;
72+ if ( $ type === null ) $ type = TypeMarker:: INT_32 ;
7073 $ packFormat = 'l ' ;
7174 } else { // INT_64
72- $ marker = 0xCB ;
75+ if ( $ type === null ) $ type = TypeMarker:: INT_64 ;
7376 $ packFormat = 'q ' ;
7477 }
7578 }
7679
77- if ($ marker === 0 ) {
80+ if ($ type === null ) {
7881 throw new \InvalidArgumentException ('Unsupported data type for vector ' );
7982 }
8083
8184 // Pack the data
8285 $ packed = [];
8386 $ littleEndian = unpack ('S ' , "\x01\x00" )[1 ] === 1 ;
8487 foreach ($ data as $ entry ) {
85- $ value = pack ($ packFormat , $ entry );
86- $ packed [] = in_array ($ packFormat , self ::$ formats ) && $ littleEndian ? strrev ($ value ) : $ value ;
88+ $ value = pack ($ packFormat , $ anyFloat ? ( float ) $ entry : ( int ) $ entry );
89+ $ packed [] = in_array ($ packFormat , self ::$ endianesFormats ) && $ littleEndian ? strrev ($ value ) : $ value ;
8790 }
8891
89- return new self (new Bytes ([chr ($ marker )]), new Bytes ($ packed ));
92+ return new self (new Bytes ([chr ($ type -> value )]), new Bytes ($ packed ));
9093 }
9194
9295 /**
93- * Decode vector structure .. returns binary $this->data as array
96+ * Decode vector structure .. returns binary $this->data as array of numbers
9497 * @return int[]|float[]
9598 * @throws \InvalidArgumentException
9699 */
97100 public function decode (): array
98101 {
99102 switch (ord ($ this ->type_marker [0 ])) {
100- case 0xC8 : // INT_8
103+ case TypeMarker:: INT_8 -> value : // INT_8
101104 $ size = 1 ;
102105 $ unpackFormat = 'c ' ;
103106 break ;
104- case 0xC9 : // INT_16
107+ case TypeMarker:: INT_16 -> value : // INT_16
105108 $ size = 2 ;
106109 $ unpackFormat = 's ' ;
107110 break ;
108- case 0xCA : // INT_32
111+ case TypeMarker:: INT_32 -> value : // INT_32
109112 $ size = 4 ;
110113 $ unpackFormat = 'l ' ;
111114 break ;
112- case 0xCB : // INT_64
115+ case TypeMarker:: INT_64 -> value : // INT_64
113116 $ size = 8 ;
114117 $ unpackFormat = 'q ' ;
115118 break ;
116- case 0xC6 : // FLOAT_32
119+ case TypeMarker:: FLOAT_32 -> value : // FLOAT_32
117120 $ size = 4 ;
118121 $ unpackFormat = 'G ' ;
119122 break ;
120- case 0xC1 : // FLOAT_64
123+ case TypeMarker:: FLOAT_64 -> value : // FLOAT_64
121124 $ size = 8 ;
122125 $ unpackFormat = 'E ' ;
123126 break ;
@@ -128,9 +131,19 @@ public function decode(): array
128131 $ output = [];
129132 $ littleEndian = unpack ('S ' , "\x01\x00" )[1 ] === 1 ;
130133 foreach (mb_str_split ((string )$ this ->data , $ size , '8bit ' ) as $ value ) {
131- $ output [] = unpack ($ unpackFormat , in_array ($ unpackFormat , self ::$ formats ) && $ littleEndian ? strrev ($ value ) : $ value )[1 ];
134+ $ output [] = unpack ($ unpackFormat , in_array ($ unpackFormat , self ::$ endianesFormats ) && $ littleEndian ? strrev ($ value ) : $ value )[1 ];
132135 }
133136
134137 return $ output ;
135138 }
136139}
140+
141+ enum TypeMarker: int
142+ {
143+ case INT_8 = 0xC8 ;
144+ case INT_16 = 0xC9 ;
145+ case INT_32 = 0xCA ;
146+ case INT_64 = 0xCB ;
147+ case FLOAT_32 = 0xC6 ;
148+ case FLOAT_64 = 0xC1 ;
149+ }
0 commit comments