better handling of unserialization with annotations
[awesomized/ext-ion] / ion_private.h
1 /*
2 +--------------------------------------------------------------------+
3 | PECL :: ion |
4 +--------------------------------------------------------------------+
5 | Redistribution and use in source and binary forms, with or without |
6 | modification, are permitted provided that the conditions mentioned |
7 | in the accompanying LICENSE file are met. |
8 +--------------------------------------------------------------------+
9 | Copyright (c) 2021, Michael Wallner <mike@php.net> |
10 +--------------------------------------------------------------------+
11 */
12
13 #include "ionc/ion.h"
14
15 #include "php.h"
16 #include "ext/date/php_date.h"
17
18 typedef struct php_ion_global_serializer php_ion_global_serializer;
19 typedef struct php_ion_global_unserializer php_ion_global_unserializer;
20
21 ZEND_BEGIN_MODULE_GLOBALS(ion)
22
23 struct php_ion_global_serializer {
24 HashTable ids;
25 HashTable tmp;
26 uint64_t level;
27 } serializer;
28
29 struct php_ion_global_unserializer {
30 HashTable ids;
31 HashTable tmp;
32 HashTable addref;
33 uint64_t level;
34 } unserializer;
35
36 ZEND_END_MODULE_GLOBALS(ion);
37
38 #ifdef ZTS
39 # define php_ion_globals (*((zend_ion_globals *) (*((void ***) tsrm_get_ls_cache()))[TSRM_UNSHUFFLE_RSRC_ID(ion_globals_id)]))
40 #else
41 # define php_ion_globals ion_globals
42 #endif
43
44 ZEND_DECLARE_MODULE_GLOBALS(ion);
45
46 static inline void php_ion_globals_serializer_init(void)
47 {
48 php_ion_global_serializer *s = &php_ion_globals.serializer;
49
50 zend_hash_init(&s->tmp, 0, NULL, ZVAL_PTR_DTOR, 0);
51 zend_hash_init(&s->ids, 0, NULL, NULL, 0);
52 }
53
54 static inline void php_ion_globals_serializer_step(void)
55 {
56 php_ion_global_serializer *s = &php_ion_globals.serializer;
57
58 if (!s->level++) {
59 zend_hash_clean(&s->ids);
60 zend_hash_clean(&s->tmp);
61 }
62 }
63
64 static inline void php_ion_globals_serializer_exit(void)
65 {
66 php_ion_global_serializer *s = &php_ion_globals.serializer;
67
68 ZEND_ASSERT(s->level);
69 if (!--s->level) {
70 zend_hash_clean(&s->ids);
71 zend_hash_clean(&s->tmp);
72 }
73 }
74
75 static inline void php_ion_globals_serializer_dtor(void)
76 {
77 php_ion_global_serializer *s = &php_ion_globals.serializer;
78
79 zend_hash_destroy(&s->tmp);
80 zend_hash_destroy(&s->ids);
81 }
82
83 void ZVAL_ADDREF(zval *zv)
84 {
85 if (Z_ISREF_P(zv)) {
86 Z_TRY_ADDREF_P(Z_REFVAL_P(zv));
87 } else {
88 Z_TRY_ADDREF_P(zv);
89 }
90 }
91 static inline void php_ion_globals_unserializer_init(void)
92 {
93 php_ion_global_unserializer *s = &php_ion_globals.unserializer;
94
95 zend_hash_init(&s->tmp, 0, NULL, ZVAL_PTR_DTOR, 0);
96 zend_hash_init(&s->ids, 0, NULL, NULL, 0);
97 zend_hash_init(&s->addref, 0, NULL, ZVAL_ADDREF, 0);
98
99 }
100
101 static inline void php_ion_globals_unserializer_step(void)
102 {
103 php_ion_global_unserializer *s = &php_ion_globals.unserializer;
104
105 if (!s->level++) {
106 zend_hash_clean(&s->addref);
107 zend_hash_clean(&s->ids);
108 zend_hash_clean(&s->tmp);
109 }
110 }
111
112 static inline void php_ion_globals_unserializer_exit(void)
113 {
114 php_ion_global_unserializer *s = &php_ion_globals.unserializer;
115
116 ZEND_ASSERT(s->level);
117 if (!--s->level) {
118 zend_hash_clean(&s->addref);
119 zend_hash_clean(&s->ids);
120 zend_hash_clean(&s->tmp);
121 }
122 }
123
124 static inline void php_ion_globals_unserializer_dtor(void)
125 {
126 php_ion_global_unserializer *s = &php_ion_globals.unserializer;
127
128 zend_hash_destroy(&s->addref);
129 zend_hash_destroy(&s->ids);
130 zend_hash_destroy(&s->tmp);
131 }
132
133 static zend_class_entry
134 *ce_Annotation,
135 *ce_Catalog,
136 *ce_Collection,
137 *ce_Decimal,
138 *ce_Decimal_Context,
139 *ce_PHP_Serializer,
140 *ce_PHP_Unserializer,
141 *ce_Reader,
142 *ce_Reader_Options,
143 *ce_Reader_Reader,
144 *ce_Reader_Buffer,
145 *ce_Reader_Stream,
146 *ce_Reader_Buffer_Reader,
147 *ce_Reader_Stream_Reader,
148 *ce_Symbol,
149 *ce_Symbol_ImportLocation,
150 *ce_Symbol_System,
151 *ce_Symbol_System_SID,
152 *ce_Symbol_Table,
153 *ce_Timestamp,
154 *ce_Type,
155 *ce_Writer,
156 *ce_Writer_Options,
157 *ce_Writer_Buffer,
158 *ce_Writer_Buffer_Writer,
159 *ce_Writer_Stream,
160 *ce_Writer_Stream_Writer,
161 *ce_Writer_Writer
162 ;
163 static zend_object_handlers
164 oh_catalog,
165 oh_decimal,
166 oh_decimal_ctx,
167 oh_reader_options,
168 oh_reader,
169 oh_symbol,
170 oh_symbol_iloc,
171 oh_symbol_table,
172 oh_timestamp,
173 oh_type,
174 oh_writer_options,
175 oh_writer
176 ;
177
178 #define php_ion_decl(type, name, ...) \
179 static zend_object *create_ion_ ## name(zend_class_entry *ce) \
180 { \
181 if (!ce) ce = ce_ ## name; \
182 php_ion_ ## type *o = ecalloc(1, sizeof(*o) + zend_object_properties_size(ce)); \
183 zend_object_std_init(&o->std, ce); \
184 object_properties_init(&o->std, ce); \
185 o->std.handlers = &oh_ ## type; \
186 return &o->std; \
187 } \
188 static void free_ion_ ## name(zend_object *std) \
189 { \
190 php_ion_ ## type *obj = php_ion_obj(type, std); \
191 __VA_ARGS__; \
192 zend_object_std_dtor(std); \
193 (void) obj; \
194 }
195 #define php_ion_register(type, name, ...) do { \
196 ce_ ## name = register_class_ion_ ## name(__VA_ARGS__); \
197 ce_ ## name ->create_object = create_ion_ ## name; \
198 memcpy(&oh_ ## type, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); \
199 oh_ ## type .offset = offsetof(php_ion_ ## type, std); \
200 oh_ ## type .free_obj = free_ion_ ## name; \
201 } while (0)
202
203 #define php_ion_obj(type, obj) \
204 ((php_ion_ ## type *) (obj ? ((char *)(obj) - XtOffsetOf(php_ion_ ## type, std)) : NULL))
205
206 #define ION_CHECK(err, ...) do { \
207 iERR __err = err; \
208 if (__err) { \
209 zend_throw_exception_ex(spl_ce_RuntimeException, __err, "%s: %s", ion_error_to_str(__err), #err); \
210 __VA_ARGS__; \
211 return; \
212 } \
213 } while (0)
214
215 #define ION_CATCH(...) do { \
216 if (EG(exception)) { \
217 __VA_ARGS__; \
218 return; \
219 } \
220 } while (0)
221
222 #define PTR_CHECK(ptr, ...) do { \
223 if (!(ptr)) { \
224 zend_throw_error(NULL, "Uninitialized object"); \
225 __VA_ARGS__; \
226 return; \
227 } \
228 } while (0)
229
230 #define OBJ_CHECK(obj) do { \
231 PTR_CHECK(obj); \
232 PTR_CHECK(*((void **)obj)); \
233 } while (0)
234
235 static inline ION_STRING *ion_string_from_cstr(ION_STRING *is, const char *s, size_t l)
236 {
237 is->length = l;
238 is->value = (BYTE *) s;
239 return is;
240 }
241
242 static inline ION_STRING *ion_string_from_zend(ION_STRING *is, const zend_string *zs)
243 {
244 is->length = ZSTR_LEN(zs);
245 is->value = (BYTE *) ZSTR_VAL(zs);
246 return is;
247 }
248
249 static inline zend_string *zend_string_from_ion(const ION_STRING *s)
250 {
251 return zend_string_init((const char *) s->value, s->length, 0);
252 }
253
254 static inline void update_property_obj(zend_object *obj, const char *n, size_t l, zend_object *p)
255 {
256 zval zobj;
257 ZVAL_OBJ(&zobj, p);
258 zend_update_property(obj->ce, obj, n, l, &zobj);
259 }
260
261 typedef struct php_ion_type {
262 ION_TYPE typ;
263 zend_object std;
264 } php_ion_type;
265
266 #define RETURN_IONTYPE(typ) do { \
267 zend_object *__zo = php_ion_type_fetch(typ); \
268 if (UNEXPECTED(!__zo)) { \
269 RETURN_THROWS(); \
270 } \
271 RETURN_OBJ_COPY(__zo); \
272 } while(0)
273
274 static inline zend_object *php_ion_type_fetch(ION_TYPE typ)
275 {
276 zend_long index = ION_TYPE_INT(typ);
277 zval *ztype = zend_hash_index_find(ce_Type->backed_enum_table, index);
278
279 if (UNEXPECTED(!ztype || Z_TYPE_P(ztype) != IS_STRING)) {
280 zend_value_error(ZEND_LONG_FMT " is not a valid backing value for enum \"%s\"", index, ZSTR_VAL(ce_Type->name));
281 return NULL;
282 }
283 return zend_enum_get_case(ce_Type, Z_STR_P(ztype));
284 }
285
286 typedef struct php_ion_symbol_iloc {
287 ION_SYMBOL_IMPORT_LOCATION loc;
288 zend_string *name;
289 zend_object std;
290 } php_ion_symbol_iloc;
291
292 static inline void php_ion_symbol_iloc_ctor(php_ion_symbol_iloc *obj)
293 {
294 zend_update_property_long(obj->std.ce, &obj->std, ZEND_STRL("location"), obj->loc.location);
295 zend_update_property_str(obj->std.ce, &obj->std, ZEND_STRL("name"), obj->name);
296 ion_string_from_zend(&obj->loc.name, obj->name);
297 }
298
299 static inline void php_ion_symbol_iloc_dtor(php_ion_symbol_iloc *obj)
300 {
301 zend_string_release(obj->name);
302 }
303
304 typedef struct php_ion_symbol {
305 ION_SYMBOL sym;
306 zend_string *value;
307 zend_object *iloc, std;
308 } php_ion_symbol;
309
310 static inline void php_ion_symbol_ctor(php_ion_symbol *obj)
311 {
312 zend_update_property_long(obj->std.ce, &obj->std, ZEND_STRL("sid"),
313 obj->sym.sid);
314 zend_update_property_str(obj->std.ce, &obj->std, ZEND_STRL("value"), obj->value);
315 ion_string_from_zend(&obj->sym.value, obj->value);
316 if (obj->iloc) {
317 update_property_obj(&obj->std, ZEND_STRL("importLocation"), obj->iloc);
318 obj->sym.import_location = php_ion_obj(symbol_iloc, obj->iloc)->loc;
319 } else {
320 zend_update_property_null(obj->std.ce, &obj->std, ZEND_STRL("importLocation"));
321 }
322 }
323
324 static inline void php_ion_symbol_dtor(php_ion_symbol *obj)
325 {
326 zend_string_release(obj->value);
327 }
328
329 static inline void php_ion_symbol_zval(ION_SYMBOL *sym_ptr, zval *return_value)
330 {
331 object_init_ex(return_value, ce_Symbol);
332 php_ion_symbol *sym = php_ion_obj(symbol, Z_OBJ_P(return_value));
333
334 sym->sym.sid = sym_ptr->sid;
335 sym->value = zend_string_from_ion(&sym_ptr->value);
336 if (!ION_SYMBOL_IMPORT_LOCATION_IS_NULL(sym_ptr)) {
337 zval ziloc;
338 object_init_ex(&ziloc, ce_Symbol_ImportLocation);
339 sym->iloc = Z_OBJ(ziloc);
340
341 php_ion_symbol_iloc *iloc = php_ion_obj(symbol_iloc, sym->iloc);
342 iloc->loc.location = sym_ptr->import_location.location;
343 iloc->name = zend_string_from_ion(&sym_ptr->import_location.name);
344
345 php_ion_symbol_iloc_ctor(iloc);
346 }
347
348 php_ion_symbol_ctor(sym);
349 }
350
351 typedef struct php_ion_symbol_table {
352 zend_object std;
353 } php_ion_symbol_table;
354
355 typedef struct php_ion_decimal_ctx {
356 decContext ctx;
357 zend_object std;
358 } php_ion_decimal_ctx;
359
360 typedef struct php_ion_decimal {
361 ION_DECIMAL dec;
362 zend_object *ctx, std;
363 } php_ion_decimal;
364
365 static inline void php_ion_decimal_ctor(php_ion_decimal *obj)
366 {
367 if (obj->ctx) {
368 update_property_obj(&obj->std, ZEND_STRL("context"), obj->ctx);
369 } else {
370 zend_update_property_null(obj->std.ce, &obj->std, ZEND_STRL("context"));
371 }
372 }
373
374 static inline void php_ion_decimal_dtor(php_ion_decimal *obj)
375 {
376 ion_decimal_free(&obj->dec);
377 }
378
379 static inline zend_string *php_ion_decimal_to_string(ION_DECIMAL *dec)
380 {
381 zend_string *zstr = zend_string_alloc(ION_DECIMAL_STRLEN(dec), 0);
382 (void) ion_decimal_to_string(dec, zstr->val);
383 return zend_string_truncate(zstr, strlen(zstr->val), 0);
384 }
385
386 static zend_array *php_ion_decimal_get_props_for(zend_object *object, zend_prop_purpose purpose)
387 {
388 php_ion_decimal *obj = php_ion_obj(decimal, object);
389
390 switch (purpose) {
391 case ZEND_PROP_PURPOSE_DEBUG:
392 zend_array *arr = zend_new_array(1);
393 zval *zv = zend_hash_str_add_empty_element(arr, ZEND_STRL("value"));
394 ZVAL_STR(zv, php_ion_decimal_to_string(&obj->dec));
395 return arr;
396
397 default:
398 return zend_std_get_properties_for(object, purpose);
399 }
400 }
401
402 typedef php_date_obj php_ion_timestamp;
403
404 static inline void php_ion_timestamp_ctor(php_ion_timestamp *obj, zend_long precision, zend_string *fmt, zend_string *dt, zval *tz)
405 {
406 if (!obj->time) {
407 php_date_initialize(obj, dt ? dt->val : "now", dt ? dt->len : 3, fmt ? fmt->val : NULL, tz, PHP_DATE_INIT_CTOR);
408 }
409 zend_update_property_long(obj->std.ce, &obj->std, ZEND_STRL("precision"), precision);
410 if (fmt) {
411 zend_update_property_str(obj->std.ce, &obj->std, ZEND_STRL("format"), fmt);
412 } else {
413 zend_update_property_stringl(obj->std.ce, &obj->std, ZEND_STRL("format"), ZEND_STRL("c"));
414 }
415 }
416
417 static inline void php_ion_timestamp_dtor(php_ion_timestamp *obj)
418 {
419 if (obj->time) {
420 timelib_time_dtor(obj->time);
421 }
422 }
423
424 static inline zend_long php_usec_from_ion(const decQuad *frac, decContext *ctx)
425 {
426 decQuad microsecs, result;
427 decQuadMultiply(&result, decQuadFromInt32(&microsecs, 1000000), frac, ctx);
428 return (zend_long) decQuadToUInt32(&result, ctx, DEC_ROUND_HALF_EVEN);
429 }
430
431 static inline decQuad *ion_ts_frac_from_usec(decQuad *frac, zend_long usec, decContext *ctx)
432 {
433 decQuad microsecs, us;
434 return decQuadDivide(frac, decQuadFromInt32(&us, usec), decQuadFromInt32(&microsecs, 1000000), ctx);
435 }
436
437 static inline timelib_time* php_time_from_ion(const ION_TIMESTAMP *ts, decContext *ctx, zend_string **fmt)
438 {
439 timelib_time *time = timelib_time_ctor();
440
441 switch (ts->precision) {
442 case ION_TS_FRAC:
443 time->us = php_usec_from_ion(&ts->fraction, ctx);
444 if (fmt) *fmt = zend_string_init(ZEND_STRL("c"), 0);
445 /* fallthrough */
446 case ION_TS_SEC:
447 time->s = ts->seconds;
448 if (fmt && !*fmt) *fmt = zend_string_init(ZEND_STRL("Y-m-d\\TH:i:sP"), 0);
449 /* fallthrough */
450 case ION_TS_MIN:
451 time->i = ts->minutes;
452 time->h = ts->hours;
453 if (fmt && !*fmt) *fmt = zend_string_init(ZEND_STRL("Y-m-d\\TH:iP"), 0);
454 /* fallthrough */
455 case ION_TS_DAY:
456 time->d = ts->day;
457 if (fmt && !*fmt) *fmt = zend_string_init(ZEND_STRL("Y-m-d\\T"), 0);
458 /* fallthrough */
459 case ION_TS_MONTH:
460 time->m = ts->month;
461 if (fmt && !*fmt) *fmt = zend_string_init(ZEND_STRL("Y-m\\T"), 0);
462 /* fallthrough */
463 case ION_TS_YEAR:
464 time->y = ts->year;
465 if (fmt && !*fmt) *fmt = zend_string_init(ZEND_STRL("Y\\T"), 0);
466 /* fallthrough */
467 default:
468 if (fmt && !*fmt) *fmt = zend_string_init(ZEND_STRL("c"), 0);
469 time->z = ts->tz_offset * 60;
470 }
471
472 return time;
473 }
474
475 static inline ION_TIMESTAMP *ion_timestamp_from_php(ION_TIMESTAMP *buf, php_ion_timestamp *ts, decContext *ctx)
476 {
477 memset(buf, 0, sizeof(*buf));
478
479 zval tmp;
480 uint8_t precision = Z_LVAL_P(zend_read_property(ts->std.ce, &ts->std, ZEND_STRL("precision"), 0, &tmp));
481
482 if (!precision || precision > ION_TS_FRAC) {
483 zend_throw_exception_ex(spl_ce_InvalidArgumentException, IERR_INVALID_ARG,
484 "Invalid precision (%u) of ion\\Timestamp", (unsigned) precision);
485 } else switch ((buf->precision = precision)) {
486 case ION_TS_FRAC:
487 ion_ts_frac_from_usec(&buf->fraction, ts->time->us, ctx);
488 /* fallthrough */
489 case ION_TS_SEC:
490 buf->seconds = ts->time->s;
491 /* fallthrough */
492 case ION_TS_MIN:
493 buf->minutes = ts->time->i;
494 /* fallthrough */
495 case ION_TS_DAY:
496 buf->hours = ts->time->h;
497 buf->day = ts->time->d;
498 /* fallthrough */
499 case ION_TS_MONTH:
500 buf->month = ts->time->m;
501 /* fallthrough */
502 case ION_TS_YEAR:
503 buf->year = ts->time->y;
504 /* fallthrough */
505 default:
506 buf->tz_offset = ts->time->z / 60;
507 }
508
509 return buf;
510 }
511
512 typedef struct php_ion_catalog {
513 ION_CATALOG *cat;
514 zend_object std;
515 } php_ion_catalog;
516
517 typedef struct php_ion_reader_options {
518 ION_READER_OPTIONS opt;
519 zend_object *cat, *dec_ctx, *cb, std;
520 } php_ion_reader_options;
521
522 typedef struct php_ion_reader {
523 ION_READER *reader;
524 ION_TYPE state;
525 enum {
526 BUFFER_READER,
527 STREAM_READER,
528 } type;
529 union {
530 zend_string *buffer;
531 struct {
532 php_stream *ptr;
533 ION_STRING buf;
534 } stream;
535 };
536 struct {
537 zend_bool call_magic_unserialize;
538 zend_string *custom_unserialize;
539 } php;
540 zend_object *opt, std;
541 } php_ion_reader;
542
543 static iERR php_ion_reader_stream_handler(struct _ion_user_stream *user)
544 {
545 php_ion_reader *reader = (php_ion_reader *) user->handler_state;
546 size_t remaining = 0, spare = reader->stream.buf.length;
547
548 if (user->curr && user->limit && (remaining = user->limit - user->curr)) {
549 memmove(reader->stream.buf.value, user->curr, remaining);
550 user->limit -= remaining;
551 spare -= remaining;
552 } else {
553 user->curr = user->limit = reader->stream.buf.value;
554 }
555
556 ssize_t read = php_stream_read(reader->stream.ptr, (char *) user->limit, spare);
557 if (EXPECTED(read > 0)) {
558 user->limit += read;
559 return IERR_OK;
560 }
561
562 if (EXPECTED(read == 0)) {
563 return IERR_EOF;
564 }
565
566 return IERR_READ_ERROR;
567 }
568
569 static iERR on_context_change(void *context, ION_COLLECTION *imports)
570 {
571 if (context) {
572 php_ion_reader *obj = php_ion_obj(reader, context);
573 (void) obj;
574 }
575 return IERR_OK;
576 }
577
578 static ION_READER_CONTEXT_CHANGE_NOTIFIER EMPTY_READER_CHANGE_NOTIFIER = {
579 on_context_change,
580 NULL
581 };
582
583 static inline void php_ion_reader_ctor(php_ion_reader *obj)
584 {
585 iERR err;
586 php_ion_reader_options *opt = php_ion_obj(reader_options, obj->opt);
587
588 if (opt) {
589 opt->opt.context_change_notifier.context = obj;
590 }
591 if (obj->type == STREAM_READER) {
592 PTR_CHECK(obj->stream.ptr);
593 GC_ADDREF(obj->stream.ptr->res);
594
595 php_ion_reader_options *opt = php_ion_obj(reader_options, obj->opt);
596 obj->stream.buf.length = opt ? opt->opt.allocation_page_size : 0x1000;
597 obj->stream.buf.value = emalloc(obj->stream.buf.length);
598 err = ion_reader_open_stream(&obj->reader, obj, php_ion_reader_stream_handler, opt ? &opt->opt : NULL);
599
600 } else {
601 err = ion_reader_open_buffer(&obj->reader,
602 (BYTE *) obj->buffer->val, obj->buffer->len,
603 opt ? &opt->opt : NULL);
604 }
605 if (opt) {
606 opt->opt.context_change_notifier.context = NULL;
607 }
608
609 ION_CHECK(err);
610 OBJ_CHECK(obj);
611 }
612 static inline void php_ion_reader_dtor(php_ion_reader *obj)
613 {
614 if (obj->reader) {
615 ion_reader_close(obj->reader);
616 }
617 if (obj->type == STREAM_READER) {
618 if (obj->stream.buf.value) {
619 efree(obj->stream.buf.value);
620 }
621 if (obj->stream.ptr) {
622 zend_list_delete(obj->stream.ptr->res);
623 }
624 } else {
625 if (obj->buffer) {
626 zend_string_release(obj->buffer);
627 }
628 }
629 }
630
631 typedef struct php_ion_writer_options {
632 ION_WRITER_OPTIONS opt;
633 zend_object *cat, *dec_ctx, *col, std;
634 } php_ion_writer_options;
635
636 typedef struct php_ion_writer {
637 ION_WRITER *writer;
638 enum {
639 BUFFER_WRITER,
640 STREAM_WRITER,
641 } type;
642 union {
643 struct {
644 zval val;
645 smart_str str;
646 } buffer;
647 struct {
648 ION_STRING buf;
649 php_stream *ptr;
650 } stream;
651 };
652 struct {
653 zend_bool call_magic_serialize;
654 zend_string *custom_serialize;
655 } php;
656 zend_object *opt, std;
657
658 } php_ion_writer;
659
660 static iERR php_ion_writer_stream_handler(struct _ion_user_stream *user)
661 {
662 php_ion_writer *writer = (php_ion_writer *) user->handler_state;
663
664 if (EXPECTED(user->limit && user->curr)) {
665 ptrdiff_t len = user->curr - writer->stream.buf.value;
666 if (len != php_stream_write(writer->stream.ptr, (char *) writer->stream.buf.value, len)) {
667 return IERR_WRITE_ERROR;
668 }
669 }
670 user->curr = writer->stream.buf.value;
671 user->limit = writer->stream.buf.value + writer->stream.buf.length;
672 return IERR_OK;
673 }
674
675 #define REF_STR() do { \
676 ZVAL_NEW_STR(ref, obj->buffer.str.s); \
677 GC_ADDREF(obj->buffer.str.s); \
678 } while (0)
679
680 #define NEW_REF_STR() do {\
681 if (Z_STR_P(ref) != obj->buffer.str.s) { \
682 zval_ptr_dtor(ref); \
683 REF_STR(); \
684 } \
685 } while(0)
686
687 static inline void php_ion_writer_stream_init(php_ion_writer *obj, php_ion_writer_options *opt)
688 {
689 PTR_CHECK(obj->stream.ptr);
690 GC_ADDREF(obj->stream.ptr->res);
691
692 obj->stream.buf.length = opt ? opt->opt.allocation_page_size : 0x1000;
693 obj->stream.buf.value = emalloc(obj->stream.buf.length);
694 }
695 static inline void php_ion_writer_buffer_init(php_ion_writer *obj)
696 {
697 zval *ref = &obj->buffer.val;
698 ZVAL_DEREF(ref);
699
700 smart_str_alloc(&obj->buffer.str, 0, 0);
701 smart_str_0(&obj->buffer.str);
702 REF_STR();
703 }
704
705 static inline void php_ion_writer_buffer_grow(php_ion_writer *obj)
706 {
707 zval *ref = &obj->buffer.val;
708 ZVAL_DEREF(ref);
709
710 switch (GC_REFCOUNT(obj->buffer.str.s)) {
711 case 2:
712 // nothing to do
713 break;
714 case 1:
715 // we've been separated
716 GC_ADDREF(obj->buffer.str.s);
717 break;
718 default:
719 // we have to separate
720 fprintf(stderr, "SEPARATE\n");
721 obj->buffer.str.s = zend_string_dup(obj->buffer.str.s, 0);
722 break;
723 }
724
725 zend_string *old = obj->buffer.str.s;
726 GC_DELREF(old);
727 smart_str_erealloc(&obj->buffer.str, obj->buffer.str.a << 1);
728 if (old == obj->buffer.str.s) {
729 GC_ADDREF(old);
730 } else if(old == Z_STR_P(ref)) {
731 ZVAL_NULL(ref);
732 }
733
734 NEW_REF_STR();
735 }
736
737 static iERR php_ion_writer_buffer_handler(struct _ion_user_stream *user)
738 {
739 php_ion_writer *writer = (php_ion_writer *) user->handler_state;
740
741 if (user->curr) {
742 writer->buffer.str.s->len += user->curr - (BYTE *) &writer->buffer.str.s->val[writer->buffer.str.s->len];
743 smart_str_0(&writer->buffer.str);
744 if (user->limit == user->curr) {
745 php_ion_writer_buffer_grow(writer);
746 }
747 }
748 user->curr = (BYTE *) &writer->buffer.str.s->val[writer->buffer.str.s->len];
749 user->limit = user->curr + writer->buffer.str.a - writer->buffer.str.s->len;
750
751 return IERR_OK;
752 }
753
754 static inline void php_ion_writer_ctor(php_ion_writer *obj)
755 {
756 if (obj->opt) {
757 update_property_obj(&obj->std, ZEND_STRL("options"), obj->opt);
758 }
759
760 php_ion_writer_options *opt = php_ion_obj(writer_options, obj->opt);
761 ION_STREAM_HANDLER h;
762 if (obj->type == STREAM_WRITER) {
763 h = php_ion_writer_stream_handler;
764 php_ion_writer_stream_init(obj, opt);
765 } else {
766 h = php_ion_writer_buffer_handler;
767 php_ion_writer_buffer_init(obj);
768 }
769
770 ION_CHECK(ion_writer_open_stream(&obj->writer, h, obj, opt ? &opt->opt : NULL));
771 OBJ_CHECK(obj);
772 }
773
774 static inline void php_ion_writer_dtor(php_ion_writer *obj)
775 {
776 if (obj->writer) {
777 ion_writer_close(obj->writer);
778 }
779 if (obj->type == STREAM_WRITER) {
780 if (obj->stream.buf.value) {
781 efree(obj->stream.buf.value);
782 }
783 if (obj->stream.ptr) {
784 zend_list_delete(obj->stream.ptr->res);
785 }
786 } else {
787 smart_str_0(&obj->buffer.str);
788 zend_string_release(obj->buffer.str.s);
789 zval_ptr_dtor(&obj->buffer.val);
790 }
791 if (obj->php.custom_serialize) {
792 zend_string_release(obj->php.custom_serialize);
793 }
794 }
795
796 static inline void php_ion_serialize_zval(php_ion_writer *, zval *);
797
798 static inline void php_ion_serialize_struct(php_ion_writer *obj, zend_array *arr)
799 {
800 ION_CHECK(ion_writer_start_container(obj->writer, tid_STRUCT));
801
802 zval *v;
803 zend_ulong h;
804 zend_string *k = NULL;
805 if (arr) ZEND_HASH_FOREACH_KEY_VAL_IND(arr, h, k, v)
806 ION_STRING is;
807 if (k) {
808 ION_CHECK(ion_writer_write_field_name(obj->writer, ion_string_from_zend(&is, k)));
809 } else {
810 char buf[MAX_LENGTH_OF_LONG + 1], *end = buf + sizeof(buf) - 1;
811 char *ptr = zend_print_long_to_buf(end, (zend_long) h);
812 ION_CHECK(ion_writer_write_field_name(obj->writer, ion_string_from_cstr(&is, ptr, end - ptr)));
813 }
814
815 php_ion_serialize_zval(obj, v);
816 ION_CATCH();
817 ZEND_HASH_FOREACH_END();
818
819 ION_CHECK(ion_writer_finish_container(obj->writer));
820 }
821
822 static inline void php_ion_serialize_list(php_ion_writer *obj, zend_array *arr)
823 {
824 ION_CHECK(ion_writer_start_container(obj->writer, tid_LIST));
825
826 zval *v;
827 ZEND_HASH_FOREACH_VAL_IND(arr, v)
828 php_ion_serialize_zval(obj, v);
829 ION_CATCH();
830 ZEND_HASH_FOREACH_END();
831
832 ION_CHECK(ion_writer_finish_container(obj->writer));
833 }
834
835 static inline void php_ion_serialize_array(php_ion_writer *obj, zend_array *arr)
836 {
837 if (zend_array_is_list(arr)) {
838 php_ion_serialize_list(obj, arr);
839 } else {
840 php_ion_serialize_struct(obj, arr);
841 }
842 }
843
844 static inline void php_ion_serialize_object_iface(php_ion_writer *obj, zend_object *zobject)
845 {
846 uint8_t *buf;
847 size_t len;
848 zval tmp;
849
850 ZVAL_OBJ(&tmp, zobject);
851 if (SUCCESS == zobject->ce->serialize(&tmp, &buf, &len, NULL)) {
852 ION_STRING is;
853 ION_CHECK(ion_writer_add_annotation(obj->writer, ion_string_from_cstr(&is, ZEND_STRL("S"))));
854 ION_CHECK(ion_writer_add_annotation(obj->writer, ion_string_from_zend(&is, zobject->ce->name)));
855 ION_CHECK(ion_writer_write_string(obj->writer, ion_string_from_cstr(&is, (char *) buf, len)));
856 efree(buf);
857 } else if (!EG(exception)){
858 zend_throw_exception_ex(spl_ce_UnexpectedValueException, IERR_INTERNAL_ERROR,
859 "Failed to serialize class %s", zobject->ce->name->val);
860 }
861 }
862
863 static inline void php_ion_serialize_object_magic(php_ion_writer *obj, zend_object *zobject, zend_function *fn)
864 {
865 zval rv;
866
867 ZVAL_NULL(&rv);
868 zend_call_method_with_0_params(zobject, zobject->ce, fn ? &fn : &zobject->ce->__serialize, "", &rv);
869 ION_CATCH();
870
871 if (IS_ARRAY == Z_TYPE(rv)) {
872 ION_STRING is;
873 ION_CHECK(ion_writer_add_annotation(obj->writer, ion_string_from_cstr(&is, fn ? "C" : "O", 1)));
874 ION_CHECK(ion_writer_add_annotation(obj->writer, ion_string_from_zend(&is, zobject->ce->name)));
875 php_ion_serialize_zval(obj, &rv);
876 zval_ptr_dtor(&rv);
877 } else {
878 zend_throw_exception_ex(spl_ce_UnexpectedValueException, IERR_INTERNAL_ERROR,
879 "%s serializer %s::%s did not return an array",
880 fn ? "Custom" : "Magic", zobject->ce->name->val,
881 fn ? fn->common.function_name->val : "__serialize");
882 }
883 }
884
885 static inline zend_string *fq_enum_case(zend_object *zobject)
886 {
887 zval *cn = zend_enum_fetch_case_name(zobject);
888 zend_string *en = zend_string_alloc(zobject->ce->name->len + Z_STRLEN_P(cn) + strlen("\\"), 0);
889 memcpy(en->val, zobject->ce->name->val, zobject->ce->name->len);
890 en->val[zobject->ce->name->len] = '\\';
891 memcpy(&en->val[zobject->ce->name->len + 1], Z_STRVAL_P(cn), Z_STRLEN_P(cn));
892 en->val[en->len] = 0;
893 return en;
894 }
895
896 static inline void php_ion_serialize_object_enum(php_ion_writer *obj, zend_object *zobject)
897 {
898 ION_STRING is;
899 ION_CHECK(ion_writer_add_annotation(obj->writer, ion_string_from_cstr(&is, ZEND_STRL("E"))));
900
901 zend_string *en = fq_enum_case(zobject);
902 ION_CHECK(ion_writer_write_string(obj->writer, ion_string_from_zend(&is, en)));
903 zend_string_release(en);
904 }
905
906 static inline void php_ion_serialize_object_std(php_ion_writer *obj, zend_object *zobject)
907 {
908 ION_STRING is;
909
910 if (zobject->ce != zend_standard_class_def) {
911 ION_CHECK(ion_writer_add_annotation(obj->writer, ion_string_from_cstr(&is, ZEND_STRL("c"))));
912 ION_CHECK(ion_writer_add_annotation(obj->writer, ion_string_from_zend(&is, zobject->ce->name)));
913 } else {
914 ION_CHECK(ion_writer_add_annotation(obj->writer, ion_string_from_cstr(&is, ZEND_STRL("o"))));
915 }
916
917 zval zobj;
918 ZVAL_OBJ(&zobj, zobject);
919 HashTable *props = zend_get_properties_for(&zobj, ZEND_PROP_PURPOSE_SERIALIZE);
920 if (props) {
921 php_ion_serialize_struct(obj, props);
922 zend_release_properties(props);
923 } else {
924 zend_throw_exception_ex(spl_ce_UnexpectedValueException, IERR_INTERNAL_ERROR,
925 "Could not get properties for serialization of class %s",
926 zobject->ce->name->val);
927 }
928 }
929
930 static inline bool can_call_magic_serialize(php_ion_writer *obj, zend_class_entry *ce)
931 {
932 if (ce->__serialize && obj->php.call_magic_serialize) {
933 return true;
934 }
935 return false;
936 }
937
938 static inline bool can_call_iface_serialize(php_ion_writer *obj, zend_class_entry *ce)
939 {
940 return !!ce->serialize;
941 }
942
943 static inline bool can_call_custom_serialize(php_ion_writer *obj, zend_object *zobject, zend_function **fn)
944 {
945 if (obj->php.custom_serialize) {
946 return !!((*fn = zend_hash_find_ptr(&zobject->ce->function_table, obj->php.custom_serialize)));
947 }
948 return false;
949 }
950
951 static inline void php_ion_serialize_object(php_ion_writer *obj, zend_object *zobject)
952 {
953 zend_function *fn;
954 zend_class_entry *ce = zobject->ce;
955 ZEND_ASSERT(ce);
956
957 if (ce->ce_flags & ZEND_ACC_NOT_SERIALIZABLE) {
958 zend_throw_exception_ex(spl_ce_InvalidArgumentException, IERR_INVALID_ARG,
959 "Serializing %s is not allowed", ce->name->val);
960 return;
961 }
962
963 if (can_call_magic_serialize(obj, ce)) {
964 php_ion_serialize_object_magic(obj, zobject, NULL);
965 } else if (can_call_iface_serialize(obj, ce)) {
966 php_ion_serialize_object_iface(obj, zobject);
967 } else if (can_call_custom_serialize(obj, zobject, &fn)) {
968 php_ion_serialize_object_magic(obj, zobject, fn);
969 } else if (zobject->ce->ce_flags & ZEND_ACC_ENUM) {
970 php_ion_serialize_object_enum(obj, zobject);
971 } else if (ce == ce_Symbol) {
972 ION_CHECK(ion_writer_write_ion_symbol(obj->writer, &php_ion_obj(symbol, zobject)->sym));
973 } else if (ce == ce_Decimal) {
974 ION_CHECK(ion_writer_write_ion_decimal(obj->writer, &php_ion_obj(decimal, zobject)->dec));
975 } else if (ce == ce_Timestamp) {
976 ION_TIMESTAMP its;
977 php_ion_timestamp *pts = php_ion_obj(timestamp, zobject);
978 php_ion_writer_options *opt = php_ion_obj(writer_options, obj->opt);
979 decContext *ctx = opt ? opt->opt.decimal_context : NULL;
980 ION_CHECK(ion_writer_write_timestamp(obj->writer, ion_timestamp_from_php(&its, pts, ctx)));
981 } else {
982 php_ion_serialize_object_std(obj, zobject);
983 }
984 }
985
986 static inline void php_ion_serialize_refcounted(php_ion_writer *obj, zval *zv)
987 {
988 zend_ulong idx = (zend_ulong) (uintptr_t) Z_COUNTED_P(zv);
989
990 ION_STRING is;
991 if (zend_hash_index_exists(&php_ion_globals.serializer.ids, idx)) {
992 zval *num = zend_hash_index_find(&php_ion_globals.serializer.ids, idx);
993
994 ION_CHECK(ion_writer_add_annotation(obj->writer, ion_string_from_cstr(&is, ZEND_STRL("r"))));
995 ION_CHECK(ion_writer_write_int64(obj->writer, Z_LVAL_P(num)));
996 } else {
997 zval num;
998
999 ZVAL_LONG(&num, zend_hash_num_elements(&php_ion_globals.serializer.ids));
1000 zend_hash_index_add(&php_ion_globals.serializer.ids, idx, &num);
1001
1002 Z_TRY_ADDREF_P(zv);
1003 zend_hash_next_index_insert(&php_ion_globals.serializer.tmp, zv);
1004
1005 switch (Z_TYPE_P(zv)) {
1006 case IS_STRING:
1007 ION_CHECK(ion_writer_write_string(obj->writer, ion_string_from_zend(&is, Z_STR_P(zv))));
1008 break;
1009
1010 case IS_ARRAY:
1011 php_ion_serialize_array(obj, Z_ARRVAL_P(zv));
1012 break;
1013
1014 case IS_OBJECT:
1015 php_ion_serialize_object(obj, Z_OBJ_P(zv));
1016 break;
1017
1018 case IS_REFERENCE:
1019 ION_CHECK(ion_writer_add_annotation(obj->writer, ion_string_from_cstr(&is, ZEND_STRL("R"))));
1020 php_ion_serialize_zval(obj, Z_REFVAL_P(zv));
1021 break;
1022 }
1023 }
1024 }
1025
1026 static inline void php_ion_serialize_zval(php_ion_writer *obj, zval *zv)
1027 {
1028 OBJ_CHECK(obj);
1029
1030 switch (Z_TYPE_P(zv)) {
1031 case IS_NULL:
1032 ION_CHECK(ion_writer_write_null(obj->writer));
1033 break;
1034 case IS_TRUE:
1035 ION_CHECK(ion_writer_write_bool(obj->writer, TRUE));
1036 break;
1037 case IS_FALSE:
1038 ION_CHECK(ion_writer_write_bool(obj->writer, FALSE));
1039 break;
1040 case IS_LONG:
1041 ION_CHECK(ion_writer_write_int64(obj->writer, Z_LVAL_P(zv)));
1042 break;
1043 case IS_DOUBLE:
1044 ION_CHECK(ion_writer_write_double(obj->writer, Z_DVAL_P(zv)));
1045 break;
1046 case IS_STRING:
1047 case IS_ARRAY:
1048 case IS_OBJECT:
1049 case IS_REFERENCE:
1050 php_ion_serialize_refcounted(obj, zv);
1051 break;
1052 default:
1053 zend_throw_exception_ex(spl_ce_InvalidArgumentException, IERR_INVALID_ARG,
1054 "Failed to serialize value of type %s", zend_zval_get_legacy_type(zv)->val);
1055 }
1056 }
1057
1058 void php_ion_serialize(php_ion_writer *obj, zval *zv, zval *return_value)
1059 {
1060 zval zwriter;
1061
1062 if (obj) {
1063 ZVAL_OBJ_COPY(&zwriter, &obj->std);
1064 } else {
1065 object_init_ex(&zwriter, ce_Writer_Buffer_Writer);
1066 obj = php_ion_obj(writer, Z_OBJ(zwriter));
1067 obj->type = BUFFER_WRITER;
1068 php_ion_writer_ctor(obj);
1069 }
1070
1071 php_ion_globals_serializer_step();
1072
1073 /* start off with a global PHP annotation instead of repeating it all over the place */
1074 ION_STRING is;
1075 ION_CHECK(ion_writer_add_annotation(obj->writer, ion_string_from_cstr(&is, ZEND_STRL("PHP"))));
1076
1077 php_ion_serialize_zval(obj, zv);
1078
1079 /* make sure to flush when done, else str.s might not contain everything until the writer is closed */
1080 ion_writer_flush(obj->writer, NULL);
1081 RETVAL_STR_COPY(obj->buffer.str.s);
1082
1083 php_ion_globals_serializer_exit();
1084
1085 zval_ptr_dtor(&zwriter);
1086 }
1087
1088 static inline void php_ion_unserialize_zval(php_ion_reader *obj, zval *return_value, ION_TYPE *typ);
1089
1090 static inline bool can_call_magic_unserialize(php_ion_reader *obj, zend_class_entry *ce)
1091 {
1092 if (ce->__unserialize && obj->php.call_magic_unserialize) {
1093 return true;
1094 }
1095 return false;
1096 }
1097
1098 static inline bool can_call_iface_unserialize(php_ion_reader *obj, zend_class_entry *ce)
1099 {
1100 return !!ce->unserialize;
1101 }
1102
1103 static inline bool can_call_custom_unserialize(php_ion_reader *obj, zend_object *zobject, zend_function **fn)
1104 {
1105 if (obj->php.custom_unserialize) {
1106 return !!((*fn = zend_hash_find_ptr(&zobject->ce->function_table, obj->php.custom_unserialize)));
1107 }
1108 return false;
1109 }
1110
1111 static inline zval *php_ion_unserialize_class(php_ion_reader *obj, zend_string *class_name, zval *return_value)
1112 {
1113 zend_class_entry *ce = zend_lookup_class(class_name);
1114
1115 if (ce) {
1116 object_init_ex(return_value, ce);
1117 return zend_hash_next_index_insert(&php_ion_globals.unserializer.ids, return_value);
1118 }
1119
1120 zend_throw_exception_ex(spl_ce_RuntimeException, IERR_IMPORT_NOT_FOUND,
1121 "Could not find class %s", class_name->val);
1122 return NULL;
1123 }
1124
1125 static inline void php_ion_unserialize_object_iface(php_ion_reader *obj, zend_string *class_name, zval *return_value)
1126 {
1127 ZEND_ASSERT(Z_TYPE_P(return_value) == IS_STRING);
1128 zend_string *s = Z_STR_P(return_value);
1129
1130 zval *backref = php_ion_unserialize_class(obj, class_name, return_value);
1131 ION_CATCH();
1132
1133 zend_class_entry *ce = Z_OBJCE_P(return_value);
1134 if (can_call_iface_unserialize(obj, ce)) {
1135 if (SUCCESS == ce->unserialize(backref, ce, (BYTE *) s->val, s->len, NULL)) {
1136 // remove all this Serializable crap in PHP-9
1137 zval_ptr_dtor(return_value);
1138 ZVAL_COPY_VALUE(return_value, backref);
1139 } else if (!EG(exception)) {
1140 zend_throw_exception_ex(spl_ce_UnexpectedValueException, IERR_INTERNAL_ERROR,
1141 "Failed to unserialize class %s", ce->name->val);
1142 }
1143 } else {
1144 zend_throw_exception_ex(spl_ce_RuntimeException, IERR_INVALID_TOKEN,
1145 "Class %s does not implement Serializable", class_name->val);
1146 }
1147
1148 zend_string_release(s);
1149 }
1150
1151 static inline void php_ion_unserialize_hash(php_ion_reader *obj, zval *return_value)
1152 {
1153 zend_hash_next_index_insert(&php_ion_globals.unserializer.ids, return_value);
1154
1155 ION_CHECK(ion_reader_step_in(obj->reader));
1156
1157 while (true) {
1158 ION_TYPE typ;
1159 ION_CHECK(ion_reader_next(obj->reader, &typ));
1160
1161 ION_STRING name;
1162 ION_CHECK(ion_reader_get_field_name(obj->reader, &name));
1163 zend_string *key = zend_string_from_ion(&name);
1164
1165 zval zvalue;
1166 php_ion_unserialize_zval(obj, &zvalue, &typ);
1167 ION_CATCH(zend_string_release(key));
1168
1169 if (typ == tid_EOF) {
1170 zend_string_release(key);
1171 break;
1172 }
1173
1174 add_assoc_zval_ex(return_value, key->val, key->len, &zvalue);
1175 zend_string_release(key);
1176 }
1177
1178 ION_CHECK(ion_reader_step_out(obj->reader));
1179 }
1180
1181 static inline void verify_unserializer(php_ion_reader *obj, uint8_t object_type,
1182 zend_string *class_name, zend_object *zobject, zend_function **fn)
1183 {
1184 switch (object_type) {
1185 case 'C':
1186 if (!can_call_custom_unserialize(obj, zobject, fn)) {
1187 zend_throw_exception_ex(spl_ce_RuntimeException, IERR_INVALID_TOKEN,
1188 "Could not find custom serializer method of %s", class_name->val);
1189 }
1190 break;
1191
1192 case 'O':
1193 if (!can_call_magic_unserialize(obj, zobject->ce)) {
1194 zend_throw_exception_ex(spl_ce_RuntimeException, IERR_INVALID_TOKEN,
1195 "Could not find method %s::__serialize()", class_name->val);
1196 }
1197 *fn = zobject->ce->__unserialize;
1198 break;
1199
1200 default:
1201 zend_throw_exception_ex(spl_ce_RuntimeException, IERR_INVALID_TOKEN,
1202 "Invalid object type %c", object_type);
1203 }
1204 }
1205 static inline void php_ion_unserialize_object(php_ion_reader *obj, uint8_t object_type, zend_string *class_name, zval *return_value)
1206 {
1207 // backup possible backref to array returned by magic/custom __serialize()
1208 zval zarr;
1209 ZVAL_COPY_VALUE(&zarr, return_value);
1210 zend_hash_next_index_insert(&php_ion_globals.unserializer.tmp, &zarr);
1211
1212 php_ion_unserialize_class(obj, class_name, return_value);
1213 ION_CATCH();
1214
1215 zend_object *zobject = Z_OBJ_P(return_value);
1216 zend_function *fn = NULL;
1217 verify_unserializer(obj, object_type, class_name, zobject, &fn);
1218 ION_CATCH();
1219
1220 if (Z_TYPE(zarr) != IS_ARRAY) {
1221 ZEND_ASSERT(Z_TYPE(zarr) != IS_OBJECT);
1222 array_init(&zarr);
1223 zend_hash_next_index_insert(&php_ion_globals.unserializer.tmp, &zarr);
1224 php_ion_unserialize_hash(obj, &zarr);
1225 ION_CATCH();
1226 }
1227
1228 zval rv;
1229 ZVAL_NULL(&rv);
1230 zend_call_method_with_1_params(zobject, zobject->ce, &fn, "", &rv, &zarr);
1231 zval_ptr_dtor(&rv);
1232 }
1233
1234 static inline void php_ion_unserialize_struct(php_ion_reader *obj, uint8_t object_type, zend_string *class_name, zval *return_value)
1235 {
1236 if (class_name) {
1237 php_ion_unserialize_object(obj, object_type, class_name, return_value);
1238 } else if (!object_type) {
1239 array_init(return_value);
1240 php_ion_unserialize_hash(obj, return_value);
1241 } else if (object_type == 'o') {
1242 object_init(return_value);
1243 php_ion_unserialize_hash(obj, return_value);
1244 } else {
1245 zend_throw_exception_ex(spl_ce_RuntimeException, IERR_INVALID_TOKEN,
1246 "Invalid object type %c", object_type);
1247 }
1248 }
1249
1250 static inline void php_ion_unserialize_list(php_ion_reader *obj, zval *return_value)
1251 {
1252 ION_CHECK(ion_reader_step_in(obj->reader));
1253 array_init(return_value);
1254 zend_hash_next_index_insert(&php_ion_globals.unserializer.ids, return_value);
1255
1256 while (true) {
1257 ION_TYPE typ;
1258 ION_CHECK(ion_reader_next(obj->reader, &typ));
1259
1260 zval next;
1261 php_ion_unserialize_zval(obj, &next, &typ);
1262 ION_CATCH();
1263
1264 if (typ == tid_EOF) {
1265 break;
1266 }
1267
1268 zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &next);
1269 }
1270
1271 ION_CHECK(ion_reader_step_out(obj->reader));
1272 }
1273
1274 static inline void php_ion_unserialize_lob(php_ion_reader *obj, zval *return_value)
1275 {
1276 zend_string *zstr = zend_string_alloc(0x1000, 0);
1277 again:
1278 SIZE read = 0;
1279 iERR err = ion_reader_read_lob_bytes(obj->reader, (BYTE *) zstr->val, zstr->len, &read);
1280 if (err == IERR_BUFFER_TOO_SMALL) {
1281 zstr = zend_string_extend(zstr, zstr->len << 2, 0);
1282 goto again;
1283 }
1284 ION_CHECK(err, zend_string_release(zstr));
1285 if (zstr->len > read) {
1286 zstr = zend_string_truncate(zstr, read, 0);
1287 }
1288 RETURN_STR(zstr);
1289 }
1290
1291 static inline void php_ion_unserialize_timestamp(php_ion_reader *obj, zval *return_value)
1292 {
1293 ION_TIMESTAMP ts;
1294 ION_CHECK(ion_reader_read_timestamp(obj->reader, &ts));
1295
1296 decContext *ctx = NULL;
1297 if (obj->opt) {
1298 ctx = php_ion_obj(reader_options, obj->opt)->opt.decimal_context;
1299 }
1300
1301 object_init_ex(return_value, ce_Timestamp);
1302 php_ion_timestamp *ts_obj = php_ion_obj(timestamp, Z_OBJ_P(return_value));
1303
1304 zend_string *fmt = NULL;
1305 ts_obj->time = php_time_from_ion(&ts, ctx, &fmt);
1306 php_ion_timestamp_ctor(ts_obj, ts.precision, fmt, NULL, NULL);
1307 zend_string_release(fmt);
1308
1309 OBJ_CHECK(ts_obj);
1310 }
1311
1312 static inline void php_ion_unserialize_int(php_ion_reader *obj, zval *return_value)
1313 {
1314 ION_INT *num = NULL;
1315 ION_CHECK(ion_int_alloc(obj->reader, &num));
1316 ION_CHECK(ion_reader_read_ion_int(obj->reader, num));
1317
1318 // TODO: SIZEOF_ZEND_LONG == 4
1319 int64_t i64;
1320 iERR err = ion_int_to_int64(num, &i64);
1321 switch (err) {
1322 case IERR_OK:
1323 RETVAL_LONG(i64);
1324 goto done;
1325
1326 case IERR_NUMERIC_OVERFLOW:
1327 SIZE max, len;
1328 ION_CHECK(ion_int_char_length(num, &max));
1329 zend_string *zs = zend_string_alloc(max-1, 0);
1330
1331 err = ion_int_to_char(num, (BYTE *) zs->val, max, &len);
1332 ZEND_ASSERT(len == zs->len);
1333 RETVAL_STR(zs);
1334 /* fall through */
1335
1336 default:
1337 done:
1338 ion_int_free(num);
1339 ION_CHECK(err);
1340 }
1341 }
1342
1343 static inline void php_ion_unserialize_backref(php_ion_reader *obj, zval *return_value)
1344 {
1345 php_ion_global_unserializer *u = &php_ion_globals.unserializer;
1346 zval *backref = zend_hash_index_find(&u->ids, Z_LVAL_P(return_value));
1347
1348 if (backref) {
1349 ZVAL_COPY_VALUE(return_value, backref);
1350 zend_hash_next_index_insert(&u->addref, return_value);
1351 } else {
1352 zend_throw_exception_ex(spl_ce_RuntimeException, IERR_INTERNAL_ERROR,
1353 "Could not find backref %ld", Z_LVAL_P(return_value));
1354 }
1355 }
1356
1357 static inline void php_ion_unserialize_zval(php_ion_reader *obj, zval *return_value, ION_TYPE *typ)
1358 {
1359 ION_TYPE typ_tmp;
1360 if (!typ) {
1361 typ = &typ_tmp;
1362 ION_CHECK(ion_reader_next(obj->reader, typ));
1363 }
1364
1365 // process any annotations
1366 bool backref = false;
1367 uint8_t object_type = 0;
1368 zend_string *object_class = NULL;
1369 int32_t ann_cnt;
1370 ION_CHECK(ion_reader_get_annotation_count(obj->reader, &ann_cnt));
1371 for (int32_t i = 0; i < ann_cnt; ++i) {
1372 ION_STRING ann_str;
1373 ION_CHECK(ion_reader_get_an_annotation(obj->reader, i, &ann_str));
1374 switch (*ann_str.value) {
1375 case 'R':
1376 ZVAL_MAKE_REF(return_value);
1377 ZVAL_DEREF(return_value);
1378 zend_hash_next_index_insert(&php_ion_globals.unserializer.addref, return_value);
1379 break;
1380
1381 case 'r':
1382 // ints
1383 backref = true;
1384 break;
1385
1386 case 'S':
1387 case 'E':
1388 // strings
1389 object_type = *ann_str.value;
1390 break;
1391
1392 case 'O':
1393 case 'C':
1394 case 'o':
1395 case 'c':
1396 // structs
1397 ION_STRING class_name;
1398 ION_CHECK(ion_reader_get_an_annotation(obj->reader, ++i, &class_name));
1399 object_class = zend_string_from_ion(&class_name);
1400 object_type = *ann_str.value;
1401 break;
1402 }
1403 }
1404
1405 BOOL bval;
1406 ION_CHECK(ion_reader_is_null(obj->reader, &bval));
1407 if (bval) {
1408 goto read_null;
1409 }
1410
1411 switch (ION_TYPE_INT(*typ)) {
1412 case tid_NULL_INT:
1413 read_null: ;
1414 ION_CHECK(ion_reader_read_null(obj->reader, typ));
1415 RETURN_NULL();
1416
1417 case tid_BOOL_INT:
1418 ION_CHECK(ion_reader_read_bool(obj->reader, &bval));
1419 RETURN_BOOL(bval);
1420
1421 case tid_INT_INT:
1422 php_ion_unserialize_int(obj, return_value);
1423 if (backref) {
1424 ION_CATCH();
1425 php_ion_unserialize_backref(obj, return_value);
1426 switch (object_type) {
1427 case 0:
1428 break;
1429 case 'S':
1430 case 'E':
1431 ION_CATCH();
1432 goto from_backref_to_string;
1433 case 'c':
1434 case 'C':
1435 case 'o':
1436 case 'O':
1437 ION_CATCH();
1438 goto from_backref_to_struct;
1439 default:
1440 ZEND_ASSERT(0);
1441 }
1442 }
1443 return;
1444
1445 case tid_FLOAT_INT:
1446 double d;
1447 ION_CHECK(ion_reader_read_double(obj->reader, &d));
1448 RETURN_DOUBLE(d);
1449
1450 case tid_DECIMAL_INT:
1451 object_init_ex(return_value, ce_Decimal);
1452 php_ion_decimal *dec = php_ion_obj(decimal, Z_OBJ_P(return_value));
1453 ION_CHECK(ion_reader_read_ion_decimal(obj->reader, &dec->dec));
1454 php_ion_decimal_ctor(dec);
1455 zend_hash_next_index_insert(&php_ion_globals.unserializer.ids, return_value);
1456 return;
1457
1458 case tid_TIMESTAMP_INT:
1459 php_ion_unserialize_timestamp(obj, return_value);
1460 zend_hash_next_index_insert(&php_ion_globals.unserializer.ids, return_value);
1461 return;
1462
1463 case tid_SYMBOL_INT:
1464 ION_SYMBOL sym;
1465 ION_CHECK(ion_reader_read_ion_symbol(obj->reader, &sym));
1466 php_ion_symbol_zval(&sym, return_value);
1467 zend_hash_next_index_insert(&php_ion_globals.unserializer.ids, return_value);
1468 return;
1469
1470 case tid_STRING_INT:
1471 ION_STRING str;
1472 ION_CHECK(ion_reader_read_string(obj->reader, &str));
1473 RETVAL_STRINGL((char *) str.value, str.length);
1474 if (object_type) {
1475 from_backref_to_string: ;
1476 zend_hash_next_index_insert(&php_ion_globals.unserializer.tmp, return_value);
1477 switch (object_type) {
1478 case 'S':
1479 php_ion_unserialize_object_iface(obj, object_class, return_value);
1480 return;
1481 case 'E':
1482 // TODO
1483 return;
1484 default:
1485 ZEND_ASSERT(0);
1486 }
1487 }
1488 zend_hash_next_index_insert(&php_ion_globals.unserializer.ids, return_value);
1489 return;
1490
1491 case tid_CLOB_INT:
1492 case tid_BLOB_INT:
1493 php_ion_unserialize_lob(obj, return_value);
1494 zend_hash_next_index_insert(&php_ion_globals.unserializer.ids, return_value);
1495 return;
1496
1497 case tid_LIST_INT:
1498 case tid_SEXP_INT: // FIXME
1499 php_ion_unserialize_list(obj, return_value);
1500 if (!object_type) {
1501 return;
1502 }
1503 /* fall through */
1504
1505 case tid_STRUCT_INT:
1506 from_backref_to_struct: ;
1507 php_ion_unserialize_struct(obj, object_type, object_class, return_value);
1508 if (object_class) {
1509 zend_string_release(object_class);
1510 }
1511 return;
1512
1513 case tid_none_INT:
1514 ZEND_ASSERT(0);
1515 break;
1516
1517 case tid_DATAGRAM_INT:
1518 ZEND_ASSERT(!"datagram");
1519 case tid_EOF_INT:
1520 return;
1521 }
1522 }
1523
1524 void php_ion_unserialize(php_ion_reader *obj, zend_string *zstr, zval *return_value)
1525 {
1526 zval zreader;
1527
1528 if (obj) {
1529 ZVAL_OBJ_COPY(&zreader, &obj->std);
1530 } else {
1531 object_init_ex(&zreader, ce_Reader_Buffer_Reader);
1532 obj = php_ion_obj(reader, Z_OBJ(zreader));
1533 obj->type = BUFFER_READER;
1534 obj->buffer = zend_string_copy(zstr);
1535 php_ion_reader_ctor(obj);
1536 }
1537
1538 php_ion_globals_unserializer_step();
1539 php_ion_unserialize_zval(obj, return_value, NULL);
1540 php_ion_globals_unserializer_exit();
1541
1542 zval_ptr_dtor(&zreader);
1543 }