1 #include "php_tarantool.h"
2
3 #include "tarantool_msgpack.h"
4 #include "tarantool_exception.h"
5
6 #include "third_party/msgpuck.h"
7
8 #if PHP_MAJOR_VERSION >= 8
9 #define TSRMLS_FETCH()
10 #endif
11
12 #ifndef HASH_KEY_NON_EXISTENT
13 #define HASH_KEY_NON_EXISTENT HASH_KEY_NON_EXISTANT
14 #endif /* HASH_KEY_NON_EXISTENT */
15
16 /* UTILITES */
17
smart_string_ensure(smart_string * str,size_t len)18 int smart_string_ensure(smart_string *str, size_t len) {
19 if (SSTR_AWA(str) > SSTR_LEN(str) + len)
20 return 0;
21 size_t needed = SSTR_AWA(str) * 2;
22 if (SSTR_LEN(str) + len > needed)
23 needed = SSTR_LEN(str) + len;
24 register size_t __n1;
25 smart_string_alloc4(str, needed, 1, __n1);
26 if (SSTR_BEG(str) == NULL)
27 return -1;
28 return 0;
29 }
30
smart_string_nullify(smart_string * str)31 void smart_string_nullify(smart_string *str) {
32 memset(SSTR_BEG(str), 0, SSTR_AWA(str));
33 }
34
35 /* PACKING ROUTINES */
36
php_mp_pack_nil(smart_string * str)37 void php_mp_pack_nil(smart_string *str) {
38 size_t needed = mp_sizeof_nil();
39 smart_string_ensure(str, needed);
40 mp_encode_nil(SSTR_POS(str));
41 SSTR_LEN(str) += needed;
42 }
43
php_mp_pack_long_pos(smart_string * str,long val)44 void php_mp_pack_long_pos(smart_string *str, long val) {
45 size_t needed = mp_sizeof_uint(val);
46 smart_string_ensure(str, needed);
47 mp_encode_uint(SSTR_POS(str), val);
48 SSTR_LEN(str) += needed;
49 }
50
php_mp_pack_long_neg(smart_string * str,long val)51 void php_mp_pack_long_neg(smart_string *str, long val) {
52 size_t needed = mp_sizeof_int(val);
53 smart_string_ensure(str, needed);
54 mp_encode_int(SSTR_POS(str), val);
55 SSTR_LEN(str) += needed;
56 }
57
php_mp_pack_long(smart_string * str,long val)58 void php_mp_pack_long(smart_string *str, long val) {
59 if (val >= 0)
60 php_mp_pack_long_pos(str, val);
61 else
62 php_mp_pack_long_neg(str, val);
63 }
64
php_mp_pack_double(smart_string * str,double val)65 void php_mp_pack_double(smart_string *str, double val) {
66 size_t needed = mp_sizeof_double(val);
67 smart_string_ensure(str, needed);
68 mp_encode_double(SSTR_POS(str), val);
69 SSTR_LEN(str) += needed;
70 }
71
php_mp_pack_bool(smart_string * str,unsigned char val)72 void php_mp_pack_bool(smart_string *str, unsigned char val) {
73 size_t needed = mp_sizeof_bool(val);
74 smart_string_ensure(str, needed);
75 mp_encode_bool(SSTR_POS(str), val);
76 SSTR_LEN(str) += needed;
77 }
78
php_mp_pack_string(smart_string * str,const char * c,size_t len)79 void php_mp_pack_string(smart_string *str, const char *c, size_t len) {
80 size_t needed = mp_sizeof_str(len);
81 smart_string_ensure(str, needed);
82 mp_encode_str(SSTR_POS(str), c, len);
83 SSTR_LEN(str) += needed;
84 }
85
php_mp_pack_hash(smart_string * str,size_t len)86 void php_mp_pack_hash(smart_string *str, size_t len) {
87 size_t needed = mp_sizeof_map(len);
88 smart_string_ensure(str, needed);
89 mp_encode_map(SSTR_POS(str), len);
90 SSTR_LEN(str) += needed;
91 }
92
php_mp_pack_array(smart_string * str,size_t len)93 void php_mp_pack_array(smart_string *str, size_t len) {
94 size_t needed = mp_sizeof_array(len);
95 smart_string_ensure(str, needed);
96 mp_encode_array(SSTR_POS(str), len);
97 SSTR_LEN(str) += needed;
98 }
99
php_mp_is_hash(zval * val)100 int php_mp_is_hash(zval *val) {
101 HashTable *ht = Z_ARRVAL_P(val);
102 int count = zend_hash_num_elements(ht);
103 if (count != ht->nNextFreeElement) {
104 return 1;
105 } else {
106 HashPosition pos = {0};
107 zend_hash_internal_pointer_reset_ex(ht, &pos);
108 int i = 0;
109 for (; i < count; ++i) {
110 if (zend_hash_get_current_key_type_ex(ht, &pos) != \
111 HASH_KEY_IS_LONG)
112 return 1;
113 zend_hash_move_forward_ex(ht, &pos);
114 }
115 }
116 return 0;
117 }
118
php_mp_pack_array_recursively(smart_string * str,zval * val)119 void php_mp_pack_array_recursively(smart_string *str, zval *val) {
120 HashTable *ht = Z_ARRVAL_P(val);
121 size_t n = zend_hash_num_elements(ht);
122
123 zval *data;
124
125 php_mp_pack_array(str, n);
126 size_t key_index = 0;
127 for (; key_index < n; ++key_index) {
128 data = zend_hash_index_find(ht, key_index);
129 if (!data || data == val || ARRAY_IS_RECURSIVE(data)) {
130 php_mp_pack_nil(str);
131 } else {
132 ARRAY_PROTECT_RECURSION(data);
133 php_mp_pack(str, data);
134 ARRAY_UNPROTECT_RECURSION(data);
135 }
136 }
137 }
138
php_mp_pack_hash_recursively(smart_string * str,zval * val)139 void php_mp_pack_hash_recursively(smart_string *str, zval *val) {
140 HashTable *ht = Z_ARRVAL_P(val);
141 size_t n = zend_hash_num_elements(ht);
142
143 zend_string *key;
144 int key_type;
145 zend_ulong key_index;
146 zval *data;
147 HashPosition pos;
148
149 php_mp_pack_hash(str, n);
150 zend_hash_internal_pointer_reset_ex(ht, &pos);
151 for (;; zend_hash_move_forward_ex(ht, &pos)) {
152 key_type = zend_hash_get_current_key_ex(ht, &key, &key_index, &pos);
153 if (key_type == HASH_KEY_NON_EXISTENT)
154 break;
155 switch (key_type) {
156 case HASH_KEY_IS_LONG:
157 php_mp_pack_long(str, key_index);
158 break;
159 case HASH_KEY_IS_STRING:
160 php_mp_pack_string(str, ZSTR_VAL(key), ZSTR_LEN(key));
161 break;
162 default:
163 /* TODO: THROW EXCEPTION */
164 php_mp_pack_string(str, "", strlen(""));
165 break;
166 }
167 data = zend_hash_get_current_data_ex(ht, &pos);
168 if (!data || data == val || ARRAY_IS_RECURSIVE(data)) {
169 php_mp_pack_nil(str);
170 } else {
171 ARRAY_PROTECT_RECURSION(data);
172 php_mp_pack(str, data);
173 ARRAY_UNPROTECT_RECURSION(data);
174 }
175 }
176 }
177
php_mp_pack(smart_string * str,zval * val)178 void php_mp_pack(smart_string *str, zval *val) {
179 if (Z_TYPE_P(val) == IS_REFERENCE)
180 val = Z_REFVAL_P(val);
181
182 switch(Z_TYPE_P(val)) {
183 case IS_NULL:
184 php_mp_pack_nil(str);
185 break;
186 case IS_LONG:
187 php_mp_pack_long(str, Z_LVAL_P(val));
188 break;
189 case IS_DOUBLE:
190 php_mp_pack_double(str, (double )Z_DVAL_P(val));
191 break;
192 case IS_TRUE:
193 case IS_FALSE:
194 php_mp_pack_bool(str, Z_TYPE_P(val) == IS_TRUE ? 1 : 0);
195 break;
196 case IS_ARRAY:
197 if (php_mp_is_hash(val))
198 php_mp_pack_hash_recursively(str, val);
199 else
200 php_mp_pack_array_recursively(str, val);
201 break;
202 case IS_STRING:
203 php_mp_pack_string(str, Z_STRVAL_P(val), Z_STRLEN_P(val));
204 break;
205 default:
206 /* TODO: THROW EXCEPTION */
207 php_mp_pack_nil(str);
208 break;
209 }
210 }
211
212 /* UNPACKING ROUTINES */
213
php_mp_unpack_nil(zval * oval,char ** str)214 ptrdiff_t php_mp_unpack_nil(zval *oval, char **str) {
215 size_t needed = mp_sizeof_nil();
216 mp_decode_nil((const char **)str);
217 ZVAL_NULL(oval);
218 str += 1;
219 return needed;
220 }
221
php_mp_unpack_uint(zval * oval,char ** str)222 ptrdiff_t php_mp_unpack_uint(zval *oval, char **str) {
223 unsigned long val = mp_decode_uint((const char **)str);
224 ZVAL_LONG(oval, val);
225 return mp_sizeof_uint(val);
226 }
227
php_mp_unpack_int(zval * oval,char ** str)228 ptrdiff_t php_mp_unpack_int(zval *oval, char **str) {
229 long val = mp_decode_int((const char **)str);
230 ZVAL_LONG(oval, val);
231 return mp_sizeof_int(val);
232 }
233
php_mp_unpack_str(zval * oval,char ** str)234 ptrdiff_t php_mp_unpack_str(zval *oval, char **str) {
235 uint32_t len = 0;
236 const char *out = mp_decode_str((const char **)str, &len);
237 ZVAL_STRINGL(oval, out, len);
238 return mp_sizeof_str(len);
239 }
240
php_mp_unpack_bin(zval * oval,char ** str)241 ptrdiff_t php_mp_unpack_bin(zval *oval, char **str) {
242 uint32_t len = 0;
243 const char *out = mp_decode_bin((const char **)str, &len);
244 char *out_alloc = emalloc(len * sizeof(char));
245 memcpy(out_alloc, out, len);
246 ZVAL_STRINGL(oval, out_alloc, len);
247 efree(out_alloc);
248 return mp_sizeof_bin(len);
249 }
250
php_mp_unpack_bool(zval * oval,char ** str)251 ptrdiff_t php_mp_unpack_bool(zval *oval, char **str) {
252 if (mp_decode_bool((const char **)str)) {
253 ZVAL_TRUE(oval);
254 } else {
255 ZVAL_FALSE(oval);
256 }
257 return mp_sizeof_bool(str);
258 }
259
php_mp_unpack_float(zval * oval,char ** str)260 ptrdiff_t php_mp_unpack_float(zval *oval, char **str) {
261 float val = mp_decode_float((const char **)str);
262 ZVAL_DOUBLE(oval, (double )val);
263 return mp_sizeof_float(val);
264 }
265
php_mp_unpack_double(zval * oval,char ** str)266 ptrdiff_t php_mp_unpack_double(zval *oval, char **str) {
267 double val = mp_decode_double((const char **)str);
268 ZVAL_DOUBLE(oval, (double )val);
269 return mp_sizeof_double(val);
270 }
271
php_mp_unpack_map(zval * oval,char ** str)272 ptrdiff_t php_mp_unpack_map(zval *oval, char **str) {
273 TSRMLS_FETCH();
274 size_t len = mp_decode_map((const char **)str);
275 array_init_size(oval, len);
276 while (len-- > 0) {
277 zval key = {0}, value = {0};
278 ZVAL_UNDEF(&key);
279 ZVAL_UNDEF(&value);
280 if (php_mp_unpack(&key, str) == FAILURE) {
281 goto error_key;
282 }
283 if (php_mp_unpack(&value, str) == FAILURE) {
284 goto error_value;
285 }
286 switch (Z_TYPE(key)) {
287 case IS_LONG:
288 add_index_zval(oval, Z_LVAL(key), &value);
289 break;
290 case IS_STRING:
291 add_assoc_zval(oval, Z_STRVAL(key), &value);
292 break;
293 case IS_DOUBLE:
294 /* convert to INT/STRING for future uses */
295 /* FALLTHROUGH */
296 default: {
297 THROW_EXC("Bad key type for PHP Array");
298 goto error;
299 }
300 }
301 zval_ptr_dtor(&key);
302 continue;
303 error:
304 zval_ptr_dtor(&value);
305 error_value:
306 zval_ptr_dtor(&key);
307 error_key:
308 zval_ptr_dtor(oval);
309 return FAILURE;
310 }
311 return SUCCESS;
312 }
313
php_mp_unpack_array(zval * oval,char ** str)314 ptrdiff_t php_mp_unpack_array(zval *oval, char **str) {
315 size_t len = mp_decode_array((const char **)str);
316 array_init_size(oval, len);
317 while (len-- > 0) {
318 zval value;
319 if (php_mp_unpack(&value, str) == FAILURE) {
320 zval_ptr_dtor(oval);
321 return FAILURE;
322 }
323 add_next_index_zval(oval, &value);
324 }
325 return SUCCESS;
326 }
327
php_mp_unpack(zval * oval,char ** str)328 ssize_t php_mp_unpack(zval *oval, char **str) {
329 size_t needed = 0;
330 switch (mp_typeof(**str)) {
331 case MP_NIL:
332 return php_mp_unpack_nil(oval, str);
333 case MP_UINT:
334 return php_mp_unpack_uint(oval, str);
335 case MP_INT:
336 return php_mp_unpack_int(oval, str);
337 case MP_STR:
338 return php_mp_unpack_str(oval, str);
339 case MP_BIN:
340 return php_mp_unpack_bin(oval, str);
341 case MP_ARRAY:
342 return php_mp_unpack_array(oval, str);
343 case MP_MAP:
344 return php_mp_unpack_map(oval, str);
345 case MP_BOOL:
346 return php_mp_unpack_bool(oval, str);
347 case MP_FLOAT:
348 return php_mp_unpack_float(oval, str);
349 case MP_DOUBLE:
350 return php_mp_unpack_double(oval, str);
351 case MP_EXT:
352 break;
353 }
354 return FAILURE;
355 }
356
357 /* SIZEOF ROUTINES */
php_mp_sizeof_nil()358 size_t php_mp_sizeof_nil() {
359 return mp_sizeof_nil();
360 }
361
php_mp_sizeof_long_pos(long val)362 size_t php_mp_sizeof_long_pos(long val) {
363 return mp_sizeof_uint(val);
364 }
365
php_mp_sizeof_long_neg(long val)366 size_t php_mp_sizeof_long_neg(long val) {
367 return mp_sizeof_int(val);
368 }
369
php_mp_sizeof_long(long val)370 size_t php_mp_sizeof_long(long val) {
371 if (val >= 0)
372 return php_mp_sizeof_long_pos(val);
373 return php_mp_sizeof_long_neg(val);
374 }
375
php_mp_sizeof_double(double val)376 size_t php_mp_sizeof_double(double val) {
377 return mp_sizeof_double(val);
378 }
379
php_mp_sizeof_bool(unsigned char val)380 size_t php_mp_sizeof_bool(unsigned char val) {
381 return mp_sizeof_bool(val);
382 }
383
php_mp_sizeof_string(size_t len)384 size_t php_mp_sizeof_string(size_t len) {
385 return mp_sizeof_str(len);
386 }
387
php_mp_sizeof_hash(size_t len)388 size_t php_mp_sizeof_hash(size_t len) {
389 return mp_sizeof_map(len);
390 }
391
php_mp_sizeof_array(size_t len)392 size_t php_mp_sizeof_array(size_t len) {
393 return mp_sizeof_array(len);
394 }
395
php_mp_sizeof_array_recursively(zval * val)396 size_t php_mp_sizeof_array_recursively(zval *val) {
397 TSRMLS_FETCH();
398 HashTable *ht = HASH_OF(val);
399 size_t n = zend_hash_num_elements(ht);
400 size_t needed = php_mp_sizeof_array(n);
401 size_t key_index = 0;
402 zval *data;
403
404 for (; key_index < n; ++key_index) {
405 data = zend_hash_index_find(ht, key_index);
406 if (!data || data == val || ARRAY_IS_RECURSIVE(data)) {
407 needed += php_mp_sizeof_nil();
408 } else {
409 ARRAY_PROTECT_RECURSION(data);
410 needed += php_mp_sizeof(data);
411 ARRAY_UNPROTECT_RECURSION(data);
412 }
413 }
414 return needed;
415 }
416
php_mp_sizeof_hash_recursively(zval * val)417 size_t php_mp_sizeof_hash_recursively(zval *val) {
418 TSRMLS_FETCH();
419 HashTable *ht = HASH_OF(val);
420 size_t n = zend_hash_num_elements(ht);
421 size_t needed = php_mp_sizeof_hash(n);
422
423 zend_string *key;
424 int key_type;
425 zend_ulong key_index;
426 zval *data;
427 HashPosition pos;
428
429 zend_hash_internal_pointer_reset_ex(ht, &pos);
430 for (;; zend_hash_move_forward_ex(ht, &pos)) {
431 key_type = zend_hash_get_current_key_ex(ht, &key, &key_index, &pos);
432 if (key_type == HASH_KEY_NON_EXISTENT)
433 break;
434 switch (key_type) {
435 case HASH_KEY_IS_LONG:
436 needed += php_mp_sizeof_long(key_index);
437 break;
438 case HASH_KEY_IS_STRING:
439 needed += php_mp_sizeof_string(ZSTR_LEN(key));
440 break;
441 default:
442 /* TODO: THROW EXCEPTION */
443 needed += php_mp_sizeof_string(strlen(""));
444 break;
445 }
446 data = zend_hash_get_current_data_ex(ht, &pos);
447 if (!data || data == val || ARRAY_IS_RECURSIVE(data)) {
448 needed += php_mp_sizeof_nil();
449 } else {
450 ARRAY_PROTECT_RECURSION(data);
451 needed += php_mp_sizeof(data);
452 ARRAY_UNPROTECT_RECURSION(data);
453 }
454 }
455 return needed;
456 }
457
458
php_mp_sizeof(zval * val)459 size_t php_mp_sizeof(zval *val) {
460 if (Z_TYPE_P(val) == IS_REFERENCE)
461 val = Z_REFVAL_P(val);
462
463 switch(Z_TYPE_P(val)) {
464 case IS_NULL:
465 return php_mp_sizeof_nil();
466 break;
467 case IS_LONG:
468 return php_mp_sizeof_long(Z_LVAL_P(val));
469 break;
470 case IS_DOUBLE:
471 return php_mp_sizeof_double((double )Z_DVAL_P(val));
472 break;
473 case IS_TRUE:
474 case IS_FALSE:
475 return php_mp_sizeof_bool(Z_TYPE_P(val) == IS_TRUE ? 1 : 0);
476 break;
477 case IS_ARRAY:
478 if (php_mp_is_hash(val))
479 return php_mp_sizeof_hash_recursively(val);
480 return php_mp_sizeof_array_recursively(val);
481 break;
482 case IS_STRING:
483 return php_mp_sizeof_string(Z_STRLEN_P(val));
484 break;
485 default:
486 /* TODO: THROW EXCEPTION */
487 return php_mp_sizeof_nil();
488 break;
489 }
490 }
491
492 /* OTHER */
493
php_mp_check(const char * str,size_t str_size)494 size_t php_mp_check(const char *str, size_t str_size) {
495 return mp_check(&str, str + str_size);
496 }
497
php_mp_pack_package_size_basic(char * pos,size_t val)498 void php_mp_pack_package_size_basic(char *pos, size_t val) {
499 *pos = 0xce;
500 *(uint32_t *)(pos + 1) = mp_bswap_u32(val);
501 }
502
php_mp_pack_package_size(smart_string * str,size_t val)503 void php_mp_pack_package_size(smart_string *str, size_t val) {
504 size_t needed = 5;
505 smart_string_ensure(str, needed);
506 php_mp_pack_package_size_basic(SSTR_POS(str), val);
507 SSTR_LEN(str) += needed;
508 }
509
php_mp_unpack_package_size(char * str)510 size_t php_mp_unpack_package_size(char* str) {
511 return mp_decode_uint((const char **)&str);
512 }
513