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