support multi sequence un/serialization
[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_serializer {
19 ION_WRITER *writer;
20 ION_WRITER_OPTIONS *options;
21 smart_str *buffer;
22
23 zend_string *call_custom;
24 zend_bool call_magic;
25 zend_bool multi_seq;
26
27 uint32_t level;
28 HashTable *ids;
29 HashTable *tmp;
30 } php_ion_serializer;
31
32 typedef struct php_ion_annotaions {
33 uint8_t backref:1;
34 uint8_t makeref:1;
35 uint8_t object_prop:1;
36 uint8_t object_type;
37 zend_string *object_class;
38 zend_string *property_class;
39 } php_ion_annotations;
40
41 typedef struct php_ion_unserializer {
42 ION_READER *reader;
43 ION_READER_OPTIONS *options;
44 ION_TYPE type;
45
46 zend_string *call_custom;
47 zend_bool call_magic;
48 zend_bool multi_seq;
49
50 uint32_t level;
51 HashTable *ids;
52 HashTable *tmp;
53 HashTable *addref;
54
55 php_ion_annotations annotations;
56 } php_ion_unserializer;
57
58 ZEND_BEGIN_MODULE_GLOBALS(ion)
59
60 php_ion_serializer serializer;
61 php_ion_unserializer unserializer;
62
63 struct {
64 HashTable serializer[2];
65 HashTable unserializer[3];
66 } _ht;
67
68 ZEND_END_MODULE_GLOBALS(ion);
69
70 #ifdef ZTS
71 # define php_ion_globals (*((zend_ion_globals *) (*((void ***) tsrm_get_ls_cache()))[TSRM_UNSHUFFLE_RSRC_ID(ion_globals_id)]))
72 #else
73 # define php_ion_globals ion_globals
74 #endif
75
76 ZEND_DECLARE_MODULE_GLOBALS(ion);
77
78 static inline void php_ion_globals_serializer_init(void)
79 {
80 php_ion_serializer *s = &php_ion_globals.serializer;
81 HashTable *h = php_ion_globals._ht.serializer;
82
83 zend_hash_init(s->tmp = &h[0], 0, NULL, ZVAL_PTR_DTOR, 0);
84 zend_hash_init(s->ids = &h[1], 0, NULL, NULL, 0);
85 }
86
87 static inline uint32_t php_ion_globals_serializer_step(void)
88 {
89 php_ion_serializer *s = &php_ion_globals.serializer;
90 uint32_t level;
91
92 if (!(level = s->level++)) {
93 zend_hash_clean(s->ids);
94 zend_hash_clean(s->tmp);
95 }
96 return level;
97 }
98
99 static inline uint32_t php_ion_globals_serializer_exit(void)
100 {
101 php_ion_serializer *s = &php_ion_globals.serializer;
102
103 ZEND_ASSERT(s->level);
104 if (!--s->level) {
105 zend_hash_clean(s->ids);
106 zend_hash_clean(s->tmp);
107 }
108 return s->level;
109 }
110
111 static inline void php_ion_globals_serializer_dtor(void)
112 {
113 php_ion_serializer *s = &php_ion_globals.serializer;
114
115 zend_hash_destroy(s->tmp);
116 zend_hash_destroy(s->ids);
117 }
118
119 void ZVAL_ADDREF(zval *zv)
120 {
121 if (Z_ISREF_P(zv)) {
122 Z_TRY_ADDREF_P(Z_REFVAL_P(zv));
123 } else {
124 Z_TRY_ADDREF_P(zv);
125 }
126 }
127 static inline void php_ion_globals_unserializer_init(void)
128 {
129 php_ion_unserializer *s = &php_ion_globals.unserializer;
130 HashTable *h = php_ion_globals._ht.unserializer;
131
132 zend_hash_init(s->tmp = &h[0], 0, NULL, ZVAL_PTR_DTOR, 0);
133 zend_hash_init(s->ids = &h[1], 0, NULL, NULL, 0);
134 zend_hash_init(s->addref = &h[2], 0, NULL, ZVAL_ADDREF, 0);
135 }
136
137 static inline void php_ion_globals_unserializer_step(void)
138 {
139 php_ion_unserializer *s = &php_ion_globals.unserializer;
140
141 if (!s->level++) {
142 zend_hash_clean(s->addref);
143 zend_hash_clean(s->ids);
144 zend_hash_clean(s->tmp);
145 }
146 }
147
148 static inline void php_ion_globals_unserializer_exit(void)
149 {
150 php_ion_unserializer *s = &php_ion_globals.unserializer;
151
152 ZEND_ASSERT(s->level);
153 if (!--s->level) {
154 zend_hash_clean(s->addref);
155 zend_hash_clean(s->ids);
156 zend_hash_clean(s->tmp);
157 }
158 }
159
160 static inline void php_ion_globals_unserializer_dtor(void)
161 {
162 php_ion_unserializer *s = &php_ion_globals.unserializer;
163
164 zend_hash_destroy(s->addref);
165 zend_hash_destroy(s->ids);
166 zend_hash_destroy(s->tmp);
167 }
168
169 static zend_class_entry
170 *ce_Annotation,
171 *ce_Catalog,
172 *ce_Collection,
173 *ce_Decimal,
174 *ce_Decimal_Context,
175 *ce_LOB,
176 *ce_Reader,
177 *ce_Reader_Options,
178 *ce_Reader_Reader,
179 *ce_Reader_Buffer,
180 *ce_Reader_Stream,
181 *ce_Reader_Buffer_Reader,
182 *ce_Reader_Stream_Reader,
183 *ce_Serializer,
184 *ce_Serializer_PHP,
185 *ce_Symbol,
186 *ce_Symbol_ImportLocation,
187 *ce_Symbol_System,
188 *ce_Symbol_System_SID,
189 *ce_Symbol_Table,
190 *ce_Timestamp,
191 *ce_Timestamp_Precision,
192 *ce_Type,
193 *ce_Unserializer,
194 *ce_Unserializer_PHP,
195 *ce_Writer,
196 *ce_Writer_Options,
197 *ce_Writer_Buffer,
198 *ce_Writer_Buffer_Writer,
199 *ce_Writer_Stream,
200 *ce_Writer_Stream_Writer,
201 *ce_Writer_Writer
202 ;
203
204 #define php_ion_decl(type, cname, ...) \
205 static zend_object_handlers oh_ ## cname; \
206 static inline zend_object *create_ion_ ## cname(zend_class_entry *ce) \
207 { \
208 if (!ce) ce = ce_ ## cname; \
209 php_ion_ ## type *o = ecalloc(1, sizeof(*o) + zend_object_properties_size(ce)); \
210 zend_object_std_init(&o->std, ce); \
211 object_properties_init(&o->std, ce); \
212 o->std.handlers = &oh_ ## cname; \
213 return &o->std; \
214 } \
215 static inline void free_ion_ ## cname(zend_object *std) \
216 { \
217 php_ion_ ## type *obj = php_ion_obj(type, std); \
218 __VA_ARGS__; \
219 zend_object_std_dtor(std); \
220 (void) obj; \
221 }
222 #define php_ion_register(type, cname, ...) do { \
223 ce_ ## cname = register_class_ion_ ## cname(__VA_ARGS__); \
224 ce_ ## cname ->create_object = create_ion_ ## cname; \
225 memcpy(&oh_ ## cname, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); \
226 oh_ ## cname .offset = offsetof(php_ion_ ## type, std); \
227 oh_ ## cname .free_obj = free_ion_ ## cname; \
228 } while (0)
229
230 #define php_ion_obj(type, obj) \
231 ((php_ion_ ## type *) (obj ? ((char *)(obj) - XtOffsetOf(php_ion_ ## type, std)) : NULL))
232
233 #define ION_CHECK(err, ...) do { \
234 iERR __err = err; \
235 if (__err) { \
236 zend_throw_exception_ex(spl_ce_RuntimeException, __err, "%s: %s", ion_error_to_str(__err), #err); \
237 __VA_ARGS__; \
238 return; \
239 } \
240 } while (0)
241
242 #define ION_CATCH(...) do { \
243 if (EG(exception)) { \
244 __VA_ARGS__; \
245 return; \
246 } \
247 } while (0)
248
249 #define PTR_CHECK(ptr, ...) do { \
250 if (!(ptr)) { \
251 zend_throw_error(NULL, "Uninitialized object"); \
252 __VA_ARGS__; \
253 return; \
254 } \
255 } while (0)
256
257 #define OBJ_CHECK(obj) do { \
258 PTR_CHECK(obj); \
259 PTR_CHECK(*((void **)obj)); \
260 } while (0)
261
262 static inline ION_STRING *ion_string_from_zend(ION_STRING *is, const zend_string *zs)
263 {
264 is->length = zs ? zs->len : 0;
265 is->value = (BYTE *) (zs ? zs->val : NULL);
266 return is;
267 }
268
269 static inline zend_string *zend_string_from_ion(const ION_STRING *s)
270 {
271 return zend_string_init((const char *) s->value, s->length, 0);
272 }
273
274 static inline void update_property_obj(zend_object *obj, const char *n, size_t l, zend_object *p)
275 {
276 zval zobj;
277 ZVAL_OBJ(&zobj, p);
278 zend_update_property(obj->ce, obj, n, l, &zobj);
279 }
280
281 typedef struct php_ion_type {
282 ION_TYPE typ;
283 zend_object std;
284 } php_ion_type;
285
286 php_ion_decl(type, Type);
287
288 #define RETURN_IONTYPE(typ) do { \
289 zend_object *__zo = php_ion_type_fetch(typ); \
290 if (UNEXPECTED(!__zo)) { \
291 RETURN_THROWS(); \
292 } \
293 RETURN_OBJ_COPY(__zo); \
294 } while(0)
295
296 static inline zend_object *php_ion_type_fetch(ION_TYPE typ)
297 {
298 zend_long index = ION_TYPE_INT(typ);
299 zval *ztype = zend_hash_index_find(ce_Type->backed_enum_table, index);
300
301 if (UNEXPECTED(!ztype || Z_TYPE_P(ztype) != IS_STRING)) {
302 zend_value_error(ZEND_LONG_FMT " is not a valid backing value for enum \"%s\"", index, ZSTR_VAL(ce_Type->name));
303 return NULL;
304 }
305 return zend_enum_get_case(ce_Type, Z_STR_P(ztype));
306 }
307
308 typedef struct php_ion_symbol_iloc {
309 ION_SYMBOL_IMPORT_LOCATION loc;
310 zend_string *name;
311 zend_object std;
312 } php_ion_symbol_iloc;
313
314 static inline void php_ion_symbol_iloc_ctor(php_ion_symbol_iloc *obj)
315 {
316 zend_update_property_long(obj->std.ce, &obj->std, ZEND_STRL("location"), obj->loc.location);
317 zend_update_property_str(obj->std.ce, &obj->std, ZEND_STRL("name"), obj->name);
318 ion_string_from_zend(&obj->loc.name, obj->name);
319 }
320
321 static inline void php_ion_symbol_iloc_dtor(php_ion_symbol_iloc *obj)
322 {
323 zend_string_release(obj->name);
324 }
325
326 php_ion_decl(symbol_iloc, Symbol_ImportLocation, php_ion_symbol_iloc_dtor(obj));
327
328 typedef struct php_ion_symbol {
329 ION_SYMBOL sym;
330 zend_string *value;
331 zend_object *iloc, std;
332 } php_ion_symbol;
333
334 static inline void php_ion_symbol_ctor(php_ion_symbol *obj)
335 {
336 zend_update_property_long(obj->std.ce, &obj->std, ZEND_STRL("sid"),
337 obj->sym.sid);
338 if (obj->value) {
339 zend_update_property_str(obj->std.ce, &obj->std, ZEND_STRL("value"), obj->value);
340 } else{
341 zend_update_property_null(obj->std.ce, &obj->std, ZEND_STRL("value"));
342 }
343 ion_string_from_zend(&obj->sym.value, obj->value);
344 if (obj->iloc) {
345 update_property_obj(&obj->std, ZEND_STRL("importLocation"), obj->iloc);
346 obj->sym.import_location = php_ion_obj(symbol_iloc, obj->iloc)->loc;
347 } else {
348 zend_update_property_null(obj->std.ce, &obj->std, ZEND_STRL("importLocation"));
349 }
350 }
351
352 static inline void php_ion_symbol_dtor(php_ion_symbol *obj)
353 {
354 if (obj->value) {
355 zend_string_release(obj->value);
356 }
357 }
358
359 static inline void php_ion_symbol_zval(ION_SYMBOL *sym_ptr, zval *return_value)
360 {
361 object_init_ex(return_value, ce_Symbol);
362 php_ion_symbol *sym = php_ion_obj(symbol, Z_OBJ_P(return_value));
363
364 sym->sym.sid = sym_ptr->sid;
365 sym->value = zend_string_from_ion(&sym_ptr->value);
366 if (!ION_SYMBOL_IMPORT_LOCATION_IS_NULL(sym_ptr)) {
367 zval ziloc;
368 object_init_ex(&ziloc, ce_Symbol_ImportLocation);
369 sym->iloc = Z_OBJ(ziloc);
370
371 php_ion_symbol_iloc *iloc = php_ion_obj(symbol_iloc, sym->iloc);
372 iloc->loc.location = sym_ptr->import_location.location;
373 iloc->name = zend_string_from_ion(&sym_ptr->import_location.name);
374
375 php_ion_symbol_iloc_ctor(iloc);
376 }
377
378 php_ion_symbol_ctor(sym);
379
380 if (!ION_SYMBOL_IMPORT_LOCATION_IS_NULL(sym_ptr)) {
381 GC_DELREF(sym->iloc);
382 }
383 }
384
385 php_ion_decl(symbol, Symbol, php_ion_symbol_dtor(obj));
386
387 typedef struct php_ion_symbol_table {
388 zend_object std;
389 } php_ion_symbol_table;
390
391 php_ion_decl(symbol_table, Symbol_Table);
392
393 typedef struct php_ion_decimal_ctx {
394 decContext ctx;
395 zend_object std;
396 } php_ion_decimal_ctx;
397
398 static inline void php_ion_decimal_ctx_ctor(php_ion_decimal_ctx *obj) {
399 zval tmp, *zbits = zend_read_property(obj->std.ce, &obj->std, ZEND_STRL("bits"), 1, &tmp);
400
401 int bits = 128;
402 if (zbits != &EG(uninitialized_zval)) {
403 bits = Z_LVAL_P(zbits);
404 } else {
405 zend_update_property_long(obj->std.ce, &obj->std, ZEND_STRL("bits"), bits);
406 }
407 switch (bits) {
408 case 32:
409 case 64:
410 case 128:
411 decContextDefault(&obj->ctx, bits);
412 break;
413 default:
414 zend_throw_exception_ex(spl_ce_InvalidArgumentException, IERR_INVALID_ARG,
415 "Decimal context only allows 32, 64 or 128 bits");
416 }
417 }
418
419 php_ion_decl(decimal_ctx, Decimal_Context);
420
421 typedef struct php_ion_decimal {
422 ION_DECIMAL dec;
423 zend_object *ctx, std;
424 } php_ion_decimal;
425
426 static inline void php_ion_decimal_from_zend_long(ION_DECIMAL *dec, decContext *ctx, zend_long num)
427 {
428 if (num <= INT32_MAX && num >= INT32_MIN) {
429 ION_CHECK(ion_decimal_from_int32(dec, num));
430 } else if (num > 0 && num <= UINT32_MAX) {
431 ION_CHECK(ion_decimal_from_uint32(dec, num));
432 } else {
433 ION_INT *iint;
434 ION_CHECK(ion_int_alloc(NULL, &iint));
435 ION_CHECK(ion_int_from_long(iint, num),
436 ion_int_free(iint));
437 /* WATCH OUT: BS API */
438 dec->type = ION_DECIMAL_TYPE_QUAD;
439 ION_CHECK(ion_decimal_from_ion_int(dec, ctx, iint),
440 ion_int_free(iint));
441 ion_int_free(iint);
442 }
443 }
444
445 static inline zend_string *php_ion_decimal_to_string(ION_DECIMAL *dec)
446 {
447 zend_string *zstr = zend_string_alloc(ION_DECIMAL_STRLEN(dec), 0);
448 (void) ion_decimal_to_string(dec, zstr->val);
449 return zend_string_truncate(zstr, strlen(zstr->val), 0);
450 }
451
452 static inline void php_ion_decimal_to_zend_long(ION_DECIMAL *dec, decContext *ctx, zend_long *l)
453 {
454 ION_INT *ii = NULL;
455 ION_CHECK(ion_int_alloc(NULL, &ii));
456 ION_CHECK(ion_decimal_to_ion_int(dec, ctx, ii), ion_int_free(ii));
457 int64_t i64;
458 ION_CHECK(ion_int_to_int64(ii, &i64), ion_int_free(ii));
459 *l = i64;
460 ion_int_free(ii);
461 }
462
463 static inline bool php_ion_decimal_fits_zend_long(php_ion_decimal *obj)
464 {
465 int32_t result;
466
467 if (!ion_decimal_is_integer(&obj->dec)) {
468 return false;
469 }
470
471 result = 1;
472 ion_decimal_compare(&obj->dec, &g_ion_dec_zend_max, &g_dec_ctx, &result);
473 if (result == 1) {
474 return false;
475 }
476 result = -1;
477 ion_decimal_compare(&obj->dec, &g_ion_dec_zend_min, &g_dec_ctx, &result);
478 if (result == -1) {
479 return false;
480 }
481 return true;
482 }
483
484 static inline void php_ion_decimal_ctor(php_ion_decimal *obj)
485 {
486 if (!obj->ctx) {
487 zval zdc;
488 object_init_ex(&zdc, ce_Decimal_Context);
489 obj->ctx = Z_OBJ(zdc);
490 php_ion_decimal_ctx_ctor(php_ion_obj(decimal_ctx, obj->ctx));
491 GC_DELREF(obj->ctx);
492 }
493 update_property_obj(&obj->std, ZEND_STRL("context"), obj->ctx);
494
495 if (php_ion_decimal_fits_zend_long(obj)) {
496 zend_long l;
497 php_ion_decimal_to_zend_long(&obj->dec, &php_ion_obj(decimal_ctx, obj->ctx)->ctx, &l);
498 zend_update_property_long(obj->std.ce, &obj->std, ZEND_STRL("number"), l);
499 } else {
500 zend_string *zstr = php_ion_decimal_to_string(&obj->dec);
501 zend_update_property_str(obj->std.ce, &obj->std, ZEND_STRL("number"), zstr);
502 zend_string_release(zstr);
503 }
504 }
505
506 static inline void php_ion_decimal_dtor(php_ion_decimal *obj)
507 {
508 ion_decimal_free(&obj->dec);
509 }
510
511 php_ion_decl(decimal, Decimal, php_ion_decimal_dtor(obj));
512
513 typedef php_date_obj php_ion_timestamp;
514
515 static inline zend_long php_usec_from_ion(const decQuad *frac, decContext *ctx)
516 {
517 decQuad microsecs, result;
518 decQuadMultiply(&result, decQuadFromInt32(&microsecs, 1000000), frac, ctx);
519 return (zend_long) decQuadToUInt32(&result, ctx, DEC_ROUND_HALF_EVEN);
520 }
521
522 static inline decQuad *ion_ts_frac_from_usec(decQuad *frac, zend_long usec, decContext *ctx)
523 {
524 decQuad microsecs, us;
525 return decQuadDivide(frac, decQuadFromInt32(&us, usec), decQuadFromInt32(&microsecs, 1000000), ctx);
526 }
527
528 static inline zend_string *php_dt_format_from_precision(uint8_t precision)
529 {
530 switch (precision) {
531 case ION_TS_FRAC:
532 return zend_string_init(ZEND_STRL("c"), 0);
533 case ION_TS_SEC:
534 return zend_string_init(ZEND_STRL("Y-m-d\\TH:i:sP"), 0);
535 case ION_TS_MIN:
536 return zend_string_init(ZEND_STRL("Y-m-d\\TH:iP"), 0);
537 case ION_TS_DAY:
538 return zend_string_init(ZEND_STRL("Y-m-d\\T"), 0);
539 case ION_TS_MONTH:
540 return zend_string_init(ZEND_STRL("Y-m\\T"), 0);
541 case ION_TS_YEAR:
542 return zend_string_init(ZEND_STRL("Y\\T"), 0);
543 default:
544 return zend_string_init(ZEND_STRL("c"), 0);
545 }
546 }
547
548 static inline timelib_time* php_time_from_ion(const ION_TIMESTAMP *ts, decContext *ctx, zend_string **fmt)
549 {
550 timelib_time *time = timelib_time_ctor();
551
552 switch (ts->precision) {
553 case ION_TS_FRAC:
554 time->us = php_usec_from_ion(&ts->fraction, ctx);
555 /* fallthrough */
556 case ION_TS_SEC:
557 time->s = ts->seconds;
558 /* fallthrough */
559 case ION_TS_MIN:
560 time->i = ts->minutes;
561 time->h = ts->hours;
562 /* fallthrough */
563 case ION_TS_DAY:
564 time->d = ts->day;
565 /* fallthrough */
566 case ION_TS_MONTH:
567 time->m = ts->month;
568 /* fallthrough */
569 case ION_TS_YEAR:
570 time->y = ts->year;
571 /* fallthrough */
572 default:
573 time->z = ts->tz_offset * 60;
574 }
575
576 if (fmt) {
577 *fmt = php_dt_format_from_precision(ts->precision);
578 }
579 return time;
580 }
581
582 static inline ION_TIMESTAMP *ion_timestamp_from_php(ION_TIMESTAMP *buf, php_ion_timestamp *ts, decContext *ctx)
583 {
584 memset(buf, 0, sizeof(*buf));
585
586 zval tmp;
587 uint8_t precision = Z_LVAL_P(zend_read_property(ts->std.ce, &ts->std, ZEND_STRL("precision"), 0, &tmp));
588
589 if (!precision || precision > ION_TS_FRAC) {
590 zend_throw_exception_ex(spl_ce_InvalidArgumentException, IERR_INVALID_ARG,
591 "Invalid precision (%u) of ion\\Timestamp", (unsigned) precision);
592 } else switch ((buf->precision = precision)) {
593 case ION_TS_FRAC:
594 ion_ts_frac_from_usec(&buf->fraction, ts->time->us, ctx);
595 /* fallthrough */
596 case ION_TS_SEC:
597 buf->seconds = ts->time->s;
598 /* fallthrough */
599 case ION_TS_MIN:
600 buf->minutes = ts->time->i;
601 /* fallthrough */
602 case ION_TS_DAY:
603 buf->hours = ts->time->h;
604 buf->day = ts->time->d;
605 /* fallthrough */
606 case ION_TS_MONTH:
607 buf->month = ts->time->m;
608 /* fallthrough */
609 case ION_TS_YEAR:
610 buf->year = ts->time->y;
611 /* fallthrough */
612 default:
613 buf->tz_offset = ts->time->z / 60;
614 }
615
616 return buf;
617 }
618
619 static inline void php_ion_timestamp_ctor(php_ion_timestamp *obj, zend_long precision, zend_string *fmt, zend_string *dt, zval *tz)
620 {
621 if (!obj->time) {
622 php_date_initialize(obj, dt ? dt->val : "", dt ? dt->len : 0, fmt ? fmt->val : NULL, tz, PHP_DATE_INIT_CTOR);
623 }
624 zend_update_property_long(obj->std.ce, &obj->std, ZEND_STRL("precision"), precision);
625
626 fmt = php_dt_format_from_precision(precision);
627 zend_update_property_str(obj->std.ce, &obj->std, ZEND_STRL("format"), fmt);
628 zend_string_release(fmt);
629 }
630
631 static inline void php_ion_timestamp_dtor(php_ion_timestamp *obj)
632 {
633 if (obj->time) {
634 timelib_time_dtor(obj->time);
635 }
636 }
637
638 php_ion_decl(timestamp, Timestamp, php_ion_timestamp_dtor(obj));
639
640 typedef struct php_ion_catalog {
641 ION_CATALOG *cat;
642 zend_object std;
643 } php_ion_catalog;
644
645 php_ion_decl(catalog, Catalog);
646
647 typedef struct php_ion_reader_options {
648 ION_READER_OPTIONS opt;
649 zend_object *cat, *dec_ctx, *cb, std;
650 } php_ion_reader_options;
651
652 php_ion_decl(reader_options, Reader_Options);
653
654 typedef struct php_ion_reader {
655 ION_READER *reader;
656 ION_TYPE state;
657 enum {
658 BUFFER_READER,
659 STREAM_READER,
660 } type;
661 union {
662 zend_string *buffer;
663 struct {
664 php_stream *ptr;
665 ION_STRING buf;
666 } stream;
667 };
668 zend_object *opt, std;
669 } php_ion_reader;
670
671 static inline iERR php_ion_reader_stream_handler(struct _ion_user_stream *user)
672 {
673 php_ion_reader *reader = (php_ion_reader *) user->handler_state;
674 size_t remaining = 0, spare = reader->stream.buf.length;
675
676 if (user->curr && user->limit && (remaining = user->limit - user->curr)) {
677 memmove(reader->stream.buf.value, user->curr, remaining);
678 user->limit -= remaining;
679 spare -= remaining;
680 } else {
681 user->curr = user->limit = reader->stream.buf.value;
682 }
683
684 ssize_t read = php_stream_read(reader->stream.ptr, (char *) user->limit, spare);
685 if (EXPECTED(read > 0)) {
686 user->limit += read;
687 return IERR_OK;
688 }
689
690 if (EXPECTED(read == 0)) {
691 return IERR_EOF;
692 }
693
694 return IERR_READ_ERROR;
695 }
696
697 static inline iERR on_context_change(void *context, ION_COLLECTION *imports)
698 {
699 if (context) {
700 php_ion_reader *obj = php_ion_obj(reader, context);
701 (void) obj;
702 }
703 return IERR_OK;
704 }
705
706 static ION_READER_CONTEXT_CHANGE_NOTIFIER EMPTY_READER_CHANGE_NOTIFIER = {
707 on_context_change,
708 NULL
709 };
710
711 static inline void php_ion_reader_ctor(php_ion_reader *obj)
712 {
713 iERR err;
714 php_ion_reader_options *opt = php_ion_obj(reader_options, obj->opt);
715
716 if (opt) {
717 opt->opt.context_change_notifier.context = obj;
718 }
719 if (obj->type == STREAM_READER) {
720 PTR_CHECK(obj->stream.ptr);
721 GC_ADDREF(obj->stream.ptr->res);
722
723 php_ion_reader_options *opt = php_ion_obj(reader_options, obj->opt);
724 obj->stream.buf.length = opt ? opt->opt.allocation_page_size : 0x1000;
725 obj->stream.buf.value = emalloc(obj->stream.buf.length);
726 err = ion_reader_open_stream(&obj->reader, obj, php_ion_reader_stream_handler, opt ? &opt->opt : NULL);
727
728 } else {
729 err = ion_reader_open_buffer(&obj->reader,
730 (BYTE *) obj->buffer->val, obj->buffer->len,
731 opt ? &opt->opt : NULL);
732 }
733 if (opt) {
734 opt->opt.context_change_notifier.context = NULL;
735 }
736
737 ION_CHECK(err);
738 OBJ_CHECK(obj);
739 }
740 static inline void php_ion_reader_dtor(php_ion_reader *obj)
741 {
742 if (obj->reader) {
743 ion_reader_close(obj->reader);
744 }
745 if (obj->type == STREAM_READER) {
746 if (obj->stream.buf.value) {
747 efree(obj->stream.buf.value);
748 }
749 if (obj->stream.ptr) {
750 zend_list_delete(obj->stream.ptr->res);
751 }
752 } else {
753 if (obj->buffer) {
754 zend_string_release(obj->buffer);
755 }
756 }
757 }
758
759 php_ion_decl(reader, Reader_Reader, php_ion_reader_dtor(obj));
760
761 typedef struct php_ion_writer_options {
762 ION_WRITER_OPTIONS opt;
763 zend_object *cat, *dec_ctx, *col, std;
764 } php_ion_writer_options;
765
766 php_ion_decl(writer_options, Writer_Options);
767
768 typedef struct php_ion_writer {
769 ION_WRITER *writer;
770 enum {
771 BUFFER_WRITER,
772 STREAM_WRITER,
773 } type;
774 union {
775 struct {
776 zval val;
777 smart_str str;
778 } buffer;
779 struct {
780 ION_STRING buf;
781 php_stream *ptr;
782 } stream;
783 };
784 zend_object *opt, std;
785
786 } php_ion_writer;
787
788 static inline iERR php_ion_writer_stream_handler(struct _ion_user_stream *user)
789 {
790 php_ion_writer *writer = (php_ion_writer *) user->handler_state;
791
792 if (EXPECTED(user->limit && user->curr)) {
793 ptrdiff_t len = user->curr - writer->stream.buf.value;
794 if (len != php_stream_write(writer->stream.ptr, (char *) writer->stream.buf.value, len)) {
795 return IERR_WRITE_ERROR;
796 }
797 }
798 user->curr = writer->stream.buf.value;
799 user->limit = writer->stream.buf.value + writer->stream.buf.length;
800 return IERR_OK;
801 }
802
803 #define REF_STR() do { \
804 ZVAL_NEW_STR(ref, obj->buffer.str.s); \
805 GC_ADDREF(obj->buffer.str.s); \
806 } while (0)
807
808 #define NEW_REF_STR() do {\
809 if (Z_STR_P(ref) != obj->buffer.str.s) { \
810 zval_ptr_dtor(ref); \
811 REF_STR(); \
812 } \
813 } while(0)
814
815 static inline void php_ion_writer_stream_init(php_ion_writer *obj, php_ion_writer_options *opt)
816 {
817 PTR_CHECK(obj->stream.ptr);
818 GC_ADDREF(obj->stream.ptr->res);
819
820 obj->stream.buf.length = opt ? opt->opt.allocation_page_size : 0x1000;
821 obj->stream.buf.value = emalloc(obj->stream.buf.length);
822 }
823 static inline void php_ion_writer_buffer_init(php_ion_writer *obj)
824 {
825 zval *ref = &obj->buffer.val;
826 ZVAL_DEREF(ref);
827
828 smart_str_alloc(&obj->buffer.str, 0, 0);
829 smart_str_0(&obj->buffer.str);
830 REF_STR();
831 }
832
833 static inline void php_ion_writer_buffer_grow(php_ion_writer *obj)
834 {
835 zval *ref = &obj->buffer.val;
836 ZVAL_DEREF(ref);
837
838 switch (GC_REFCOUNT(obj->buffer.str.s)) {
839 case 2:
840 // nothing to do
841 break;
842 case 1:
843 // we've been separated
844 GC_ADDREF(obj->buffer.str.s);
845 break;
846 default:
847 // we have to separate
848 fprintf(stderr, "SEPARATE\n");
849 obj->buffer.str.s = zend_string_dup(obj->buffer.str.s, 0);
850 break;
851 }
852
853 zend_string *old = obj->buffer.str.s;
854 GC_DELREF(old);
855 smart_str_erealloc(&obj->buffer.str, obj->buffer.str.a << 1);
856 if (old == obj->buffer.str.s) {
857 GC_ADDREF(old);
858 } else if(old == Z_STR_P(ref)) {
859 ZVAL_NULL(ref);
860 }
861
862 NEW_REF_STR();
863 }
864
865 static inline iERR php_ion_writer_buffer_handler(struct _ion_user_stream *user)
866 {
867 php_ion_writer *writer = (php_ion_writer *) user->handler_state;
868
869 if (user->curr) {
870 writer->buffer.str.s->len += user->curr - (BYTE *) &writer->buffer.str.s->val[writer->buffer.str.s->len];
871 smart_str_0(&writer->buffer.str);
872 if (user->limit == user->curr) {
873 php_ion_writer_buffer_grow(writer);
874 }
875 }
876 user->curr = (BYTE *) &writer->buffer.str.s->val[writer->buffer.str.s->len];
877 user->limit = user->curr + writer->buffer.str.a - writer->buffer.str.s->len;
878
879 return IERR_OK;
880 }
881
882 static inline void php_ion_writer_ctor(php_ion_writer *obj)
883 {
884 if (obj->opt) {
885 update_property_obj(&obj->std, ZEND_STRL("options"), obj->opt);
886 }
887
888 php_ion_writer_options *opt = php_ion_obj(writer_options, obj->opt);
889 ION_STREAM_HANDLER h;
890 if (obj->type == STREAM_WRITER) {
891 h = php_ion_writer_stream_handler;
892 php_ion_writer_stream_init(obj, opt);
893 } else {
894 h = php_ion_writer_buffer_handler;
895 php_ion_writer_buffer_init(obj);
896 }
897
898 ION_CHECK(ion_writer_open_stream(&obj->writer, h, obj, opt ? &opt->opt : NULL));
899 OBJ_CHECK(obj);
900 }
901
902 static inline void php_ion_writer_dtor(php_ion_writer *obj)
903 {
904 if (obj->writer) {
905 ion_writer_close(obj->writer);
906 }
907 if (obj->type == STREAM_WRITER) {
908 if (obj->stream.buf.value) {
909 efree(obj->stream.buf.value);
910 }
911 if (obj->stream.ptr) {
912 zend_list_delete(obj->stream.ptr->res);
913 }
914 } else {
915 if (obj->buffer.str.s) {
916 smart_str_0(&obj->buffer.str);
917 zend_string_release(obj->buffer.str.s);
918 }
919 zval_ptr_dtor(&obj->buffer.val);
920 }
921 }
922
923 php_ion_decl(writer, Writer_Writer, php_ion_writer_dtor(obj));
924
925 typedef struct php_ion_serializer_php {
926 php_ion_serializer serializer;
927 zend_object *opt, std;
928 } php_ion_serializer_php;
929
930 static inline void php_ion_serializer_php_ctor(php_ion_serializer_php *ser_obj)
931 {
932 php_ion_serializer *global_ser = &php_ion_globals.serializer;
933 ser_obj->serializer.ids = global_ser->ids;
934 ser_obj->serializer.tmp = global_ser->tmp;
935
936 zend_update_property_bool(ser_obj->std.ce, &ser_obj->std, ZEND_STRL("callMagicSerialize"),
937 ser_obj->serializer.call_magic);
938 if (ser_obj->serializer.call_custom) {
939 zend_update_property_str(ser_obj->std.ce, &ser_obj->std, ZEND_STRL("callCustomSerialize"),
940 ser_obj->serializer.call_custom);
941 ser_obj->serializer.call_custom = zend_string_tolower(ser_obj->serializer.call_custom);
942 } else {
943 zend_update_property_null(ser_obj->std.ce, &ser_obj->std, ZEND_STRL("callCustomSerialize"));
944 }
945 if (ser_obj->opt) {
946 php_ion_writer_options *o_woptions = php_ion_obj(writer_options, ser_obj->opt);
947 ser_obj->serializer.options = &o_woptions->opt;
948 update_property_obj(&ser_obj->std, ZEND_STRL("writerOptions"), ser_obj->opt);
949 } else {
950 zend_update_property_null(ser_obj->std.ce, &ser_obj->std, ZEND_STRL("writerOptions"));
951 }
952 }
953
954 static inline void php_ion_serializer_php_dtor(php_ion_serializer_php *obj)
955 {
956 if (obj->serializer.call_custom) {
957 zend_string_release(obj->serializer.call_custom);
958 }
959 }
960
961 static inline void php_ion_serialize_zval(php_ion_serializer *, zval *);
962
963 static inline void php_ion_serialize_struct(php_ion_serializer *ser, zend_array *arr, bool props)
964 {
965 ION_CHECK(ion_writer_start_container(ser->writer, tid_STRUCT));
966
967 zval *v;
968 zend_ulong h;
969 zend_string *k = NULL;
970 if (arr) ZEND_HASH_FOREACH_KEY_VAL_IND(arr, h, k, v)
971 ION_STRING is;
972 if (k) {
973 size_t prop_len;
974 const char *class_name, *prop_name;
975 if (props && (SUCCESS == zend_unmangle_property_name_ex(k, &class_name, &prop_name, &prop_len)) && class_name) {
976 ION_CHECK(ion_writer_add_annotation(ser->writer, ion_string_assign_cstr(&is, ZEND_STRL("p"))));
977 ION_CHECK(ion_writer_add_annotation(ser->writer, ion_string_assign_cstr(&is, (char *) class_name, prop_name - class_name - 1)));
978 } else {
979 prop_name = k->val;
980 prop_len = k->len;
981 }
982 ION_CHECK(ion_writer_write_field_name(ser->writer, ion_string_assign_cstr(&is, (char *) prop_name, prop_len)));
983 } else {
984 char buf[MAX_LENGTH_OF_LONG + 1], *end = buf + sizeof(buf) - 1;
985 char *ptr = zend_print_long_to_buf(end, (zend_long) h);
986 ION_CHECK(ion_writer_write_field_name(ser->writer, ion_string_assign_cstr(&is, ptr, end - ptr)));
987 }
988
989 php_ion_serialize_zval(ser, v);
990 ION_CATCH();
991 ZEND_HASH_FOREACH_END();
992
993 ION_CHECK(ion_writer_finish_container(ser->writer));
994 }
995
996 static inline void php_ion_serialize_list(php_ion_serializer *ser, zend_array *arr)
997 {
998 ION_CHECK(ion_writer_start_container(ser->writer, tid_LIST));
999
1000 zval *v;
1001 ZEND_HASH_FOREACH_VAL_IND(arr, v)
1002 php_ion_serialize_zval(ser, v);
1003 ION_CATCH();
1004 ZEND_HASH_FOREACH_END();
1005
1006 ION_CHECK(ion_writer_finish_container(ser->writer));
1007 }
1008
1009 static inline void php_ion_serialize_array(php_ion_serializer *ser, zend_array *arr)
1010 {
1011 if (zend_array_is_list(arr)) {
1012 php_ion_serialize_list(ser, arr);
1013 } else {
1014 php_ion_serialize_struct(ser, arr, false);
1015 }
1016 }
1017
1018 static inline void php_ion_serialize_object_iface(php_ion_serializer *ser, zend_object *zobject)
1019 {
1020 uint8_t *buf;
1021 size_t len;
1022 zval tmp;
1023
1024 ZVAL_OBJ(&tmp, zobject);
1025 if (SUCCESS == zobject->ce->serialize(&tmp, &buf, &len, NULL)) {
1026 ION_STRING is;
1027 ION_CHECK(ion_writer_add_annotation(ser->writer, ion_string_assign_cstr(&is, ZEND_STRL("S"))));
1028 ION_CHECK(ion_writer_add_annotation(ser->writer, ion_string_from_zend(&is, zobject->ce->name)));
1029 ION_CHECK(ion_writer_write_string(ser->writer, ion_string_assign_cstr(&is, (char *) buf, len)));
1030 efree(buf);
1031 } else if (!EG(exception)){
1032 zend_throw_exception_ex(spl_ce_UnexpectedValueException, IERR_INTERNAL_ERROR,
1033 "Failed to serialize class %s", zobject->ce->name->val);
1034 }
1035 }
1036
1037 static inline void php_ion_serialize_object_magic(php_ion_serializer *ser, zend_object *zobject, zend_function *fn)
1038 {
1039 zval rv;
1040
1041 ZVAL_NULL(&rv);
1042 zend_call_known_instance_method_with_0_params(fn ? fn : zobject->ce->__serialize, zobject, &rv);
1043 ION_CATCH();
1044
1045 if (IS_ARRAY == Z_TYPE(rv)) {
1046 ION_STRING is;
1047 ION_CHECK(ion_writer_add_annotation(ser->writer, ion_string_assign_cstr(&is, fn ? "C" : "O", 1)));
1048 ION_CHECK(ion_writer_add_annotation(ser->writer, ion_string_from_zend(&is, zobject->ce->name)));
1049 php_ion_serialize_zval(ser, &rv);
1050 zval_ptr_dtor(&rv);
1051 } else {
1052 zend_throw_exception_ex(spl_ce_UnexpectedValueException, IERR_INTERNAL_ERROR,
1053 "%s serializer %s::%s did not return an array",
1054 fn ? "Custom" : "Magic", zobject->ce->name->val,
1055 fn ? fn->common.function_name->val : "__serialize");
1056 }
1057 }
1058
1059 static inline void php_ion_serialize_object_enum(php_ion_serializer *ser, zend_object *zobject)
1060 {
1061 ION_STRING is;
1062 ION_CHECK(ion_writer_add_annotation(ser->writer, ion_string_assign_cstr(&is, ZEND_STRL("E"))));
1063
1064 ION_CHECK(ion_writer_add_annotation(ser->writer, ion_string_from_zend(&is, zobject->ce->name)));
1065 zval *z_cname = zend_enum_fetch_case_name(zobject);
1066 ION_CHECK(ion_writer_write_symbol(ser->writer, ion_string_from_zend(&is, Z_STR_P(z_cname))));
1067 }
1068
1069 static inline void php_ion_serialize_object_std(php_ion_serializer *ser, zend_object *zobject)
1070 {
1071 ION_STRING is;
1072
1073 if (zobject->ce != zend_standard_class_def) {
1074 ION_CHECK(ion_writer_add_annotation(ser->writer, ion_string_assign_cstr(&is, ZEND_STRL("c"))));
1075 ION_CHECK(ion_writer_add_annotation(ser->writer, ion_string_from_zend(&is, zobject->ce->name)));
1076 } else {
1077 ION_CHECK(ion_writer_add_annotation(ser->writer, ion_string_assign_cstr(&is, ZEND_STRL("o"))));
1078 }
1079
1080 zval zobj;
1081 ZVAL_OBJ(&zobj, zobject);
1082 HashTable *props = zend_get_properties_for(&zobj, ZEND_PROP_PURPOSE_SERIALIZE);
1083 if (props) {
1084 php_ion_serialize_struct(ser, props, true);
1085 zend_release_properties(props);
1086 } else {
1087 zend_throw_exception_ex(spl_ce_UnexpectedValueException, IERR_INTERNAL_ERROR,
1088 "Could not get properties for serialization of class %s",
1089 zobject->ce->name->val);
1090 }
1091 }
1092
1093 static inline void php_ion_serialize_object_lob(php_ion_serializer *ser, zend_object *zobject)
1094 {
1095 zval tmp_type, *type = zend_read_property(NULL, zobject, ZEND_STRL("type"), 0, &tmp_type);
1096 zval tmp_value, *value = zend_read_property(NULL, zobject, ZEND_STRL("value"), 0, &tmp_value);
1097 switch (Z_LVAL_P(zend_enum_fetch_case_value(Z_OBJ_P(type)))) {
1098 case tid_BLOB_INT:
1099 ION_CHECK(ion_writer_write_blob(ser->writer, (BYTE *) Z_STRVAL_P(value), Z_STRLEN_P(value)));
1100 break;
1101 case tid_CLOB_INT:
1102 ION_CHECK(ion_writer_write_clob(ser->writer, (BYTE *) Z_STRVAL_P(value), Z_STRLEN_P(value)));
1103 break;
1104 default:
1105 zend_throw_exception_ex(spl_ce_InvalidArgumentException, IERR_INVALID_ARG,
1106 "Unsupported LOB type: ion\\Type::%s", Z_STRVAL_P(zend_enum_fetch_case_name(Z_OBJ_P(type))));
1107 break;
1108 }
1109 }
1110
1111 static inline bool can_call_magic_serialize(php_ion_serializer *ser, zend_class_entry *ce)
1112 {
1113 return ce->__serialize && ser->call_magic;
1114 }
1115
1116 static inline bool can_call_iface_serialize(php_ion_serializer *, zend_class_entry *ce)
1117 {
1118 return !!ce->serialize;
1119 }
1120
1121 static inline bool can_call_custom_serialize(php_ion_serializer *ser, zend_object *zobject, zend_function **fn)
1122 {
1123 if (ser->call_custom) {
1124 return !!(*fn = zend_hash_find_ptr(&zobject->ce->function_table, ser->call_custom));
1125 }
1126 return false;
1127 }
1128
1129 static inline void php_ion_serialize_object(php_ion_serializer *ser, zend_object *zobject)
1130 {
1131 zend_function *fn;
1132 zend_class_entry *ce = zobject->ce;
1133 ZEND_ASSERT(ce);
1134
1135 if (ce->ce_flags & ZEND_ACC_NOT_SERIALIZABLE) {
1136 zend_throw_exception_ex(spl_ce_InvalidArgumentException, IERR_INVALID_ARG,
1137 "Serializing %s is not allowed", ce->name->val);
1138 return;
1139 }
1140
1141 if (can_call_magic_serialize(ser, ce)) {
1142 php_ion_serialize_object_magic(ser, zobject, NULL);
1143 } else if (can_call_iface_serialize(ser, ce)) {
1144 php_ion_serialize_object_iface(ser, zobject);
1145 } else if (can_call_custom_serialize(ser, zobject, &fn)) {
1146 php_ion_serialize_object_magic(ser, zobject, fn);
1147 } else if (zobject->ce->ce_flags & ZEND_ACC_ENUM) {
1148 php_ion_serialize_object_enum(ser, zobject);
1149 } else if (ce == ce_Symbol) {
1150 ION_CHECK(ion_writer_write_ion_symbol(ser->writer, &php_ion_obj(symbol, zobject)->sym));
1151 } else if (ce == ce_Decimal) {
1152 ION_CHECK(ion_writer_write_ion_decimal(ser->writer, &php_ion_obj(decimal, zobject)->dec));
1153 } else if (ce == ce_Timestamp) {
1154 ION_TIMESTAMP its;
1155 php_ion_timestamp *pts = php_ion_obj(timestamp, zobject);
1156 decContext *ctx = ser->options ? ser->options->decimal_context : NULL;
1157 ION_CHECK(ion_writer_write_timestamp(ser->writer, ion_timestamp_from_php(&its, pts, ctx)));
1158 } else if (ce == ce_LOB) {
1159 php_ion_serialize_object_lob(ser, zobject);
1160 } else {
1161 php_ion_serialize_object_std(ser, zobject);
1162 }
1163 }
1164
1165 static inline void php_ion_serialize_refcounted(php_ion_serializer *ser, zval *zv)
1166 {
1167 zend_ulong idx = (zend_ulong) (uintptr_t) Z_COUNTED_P(zv);
1168
1169 ION_STRING is;
1170 if (zend_hash_index_exists(ser->ids, idx)) {
1171 zval *num = zend_hash_index_find(ser->ids, idx);
1172
1173 ION_CHECK(ion_writer_add_annotation(ser->writer, ion_string_assign_cstr(&is, ZEND_STRL("r"))));
1174 ION_CHECK(ion_writer_write_int64(ser->writer, Z_LVAL_P(num)));
1175 } else {
1176 zval num;
1177
1178 ZVAL_LONG(&num, zend_hash_num_elements(ser->ids));
1179 zend_hash_index_add(ser->ids, idx, &num);
1180
1181 Z_TRY_ADDREF_P(zv);
1182 zend_hash_next_index_insert(ser->tmp, zv);
1183
1184 switch (Z_TYPE_P(zv)) {
1185 case IS_STRING:
1186 ION_CHECK(ion_writer_write_string(ser->writer, ion_string_from_zend(&is, Z_STR_P(zv))));
1187 break;
1188
1189 case IS_ARRAY:
1190 php_ion_serialize_array(ser, Z_ARRVAL_P(zv));
1191 break;
1192
1193 case IS_OBJECT:
1194 php_ion_serialize_object(ser, Z_OBJ_P(zv));
1195 break;
1196
1197 case IS_REFERENCE:
1198 ION_CHECK(ion_writer_add_annotation(ser->writer, ion_string_assign_cstr(&is, ZEND_STRL("R"))));
1199 php_ion_serialize_zval(ser, Z_REFVAL_P(zv));
1200 break;
1201 }
1202 }
1203 }
1204
1205 static inline void php_ion_serialize_zval(php_ion_serializer *ser, zval *zv)
1206 {
1207 OBJ_CHECK(ser);
1208
1209 switch (Z_TYPE_P(zv)) {
1210 case IS_NULL:
1211 ION_CHECK(ion_writer_write_null(ser->writer));
1212 break;
1213 case IS_TRUE:
1214 ION_CHECK(ion_writer_write_bool(ser->writer, TRUE));
1215 break;
1216 case IS_FALSE:
1217 ION_CHECK(ion_writer_write_bool(ser->writer, FALSE));
1218 break;
1219 case IS_LONG:
1220 ION_CHECK(ion_writer_write_int64(ser->writer, Z_LVAL_P(zv)));
1221 break;
1222 case IS_DOUBLE:
1223 ION_CHECK(ion_writer_write_double(ser->writer, Z_DVAL_P(zv)));
1224 break;
1225 case IS_STRING:
1226 case IS_ARRAY:
1227 case IS_OBJECT:
1228 case IS_REFERENCE:
1229 php_ion_serialize_refcounted(ser, zv);
1230 break;
1231 default:
1232 zend_throw_exception_ex(spl_ce_InvalidArgumentException, IERR_INVALID_ARG,
1233 "Failed to serialize value of type %s", zend_zval_type_name(zv));
1234 }
1235 }
1236
1237 php_ion_decl(serializer_php, Serializer_PHP, php_ion_serializer_php_dtor(obj));
1238
1239 static inline void php_ion_serialize_ex(php_ion_serializer *ser, zval *zv)
1240 {
1241 HashPosition pos;
1242 HashTable *arr = NULL;
1243
1244 if (ser->multi_seq) {
1245 if (Z_TYPE_P(zv) != IS_ARRAY || !zend_array_is_list(Z_ARRVAL_P(zv))) {
1246 zend_throw_exception_ex(spl_ce_InvalidArgumentException, IERR_INVALID_ARG,
1247 "Expected a packed, consecutively numerically indexed array as argument to the multi sequence serializer");
1248 return;
1249 }
1250
1251 arr = Z_ARRVAL_P(zv);
1252
1253 zend_hash_internal_pointer_reset_ex(arr, &pos);
1254 zv = zend_hash_get_current_data_ex(arr, &pos);
1255 }
1256
1257 while (zv) {
1258 /* start off with a global PHP annotation instead of repeating it all over the place */
1259 if (0 == php_ion_globals_serializer_step()) {
1260 ION_STRING is;
1261 ION_CHECK(ion_writer_add_annotation(ser->writer, ion_string_assign_cstr(&is, ZEND_STRL("PHP"))));
1262 }
1263 php_ion_serialize_zval(ser, zv);
1264 php_ion_globals_serializer_exit();
1265
1266 if (!ser->multi_seq) {
1267 break;
1268 }
1269 zend_hash_move_forward_ex(arr, &pos);
1270 zv = zend_hash_get_current_data_ex(arr, &pos);
1271 }
1272 }
1273
1274 void php_ion_serialize(php_ion_serializer *ser, zval *zv, zval *return_value)
1275 {
1276 zend_object *zo_opt = NULL, *zo_ser = NULL;
1277
1278 if (!ser) {
1279 zo_ser = create_ion_Serializer_PHP(NULL);
1280 php_ion_serializer_php *o_ser = php_ion_obj(serializer_php, zo_ser);
1281 PTR_CHECK(o_ser);
1282 o_ser->serializer.call_magic = true;
1283 php_ion_serializer_php_ctor(o_ser);
1284 ION_CATCH();
1285 ser = &o_ser->serializer;
1286 }
1287
1288 zend_object *zo_writer = create_ion_Writer_Writer(ce_Writer_Buffer_Writer);
1289 php_ion_writer *writer = php_ion_obj(writer, zo_writer);
1290 writer->type = BUFFER_WRITER;
1291
1292 if (ser->options) {
1293 zo_opt = writer->opt = create_ion_Writer_Options(NULL);
1294 php_ion_obj(writer_options, writer->opt)->opt = *ser->options;
1295 }
1296
1297 php_ion_writer_ctor(writer);
1298 ser->writer = writer->writer;
1299 ser->buffer = &writer->buffer.str;
1300
1301 if (!EG(exception)) {
1302 php_ion_serialize_ex(ser, zv);
1303 }
1304
1305 /* make sure to flush when done, else str.s might not contain everything until the writer is closed */
1306 ion_writer_flush(ser->writer, NULL);
1307 RETVAL_STR_COPY(ser->buffer->s);
1308
1309 OBJ_RELEASE(zo_writer);
1310 if (zo_opt) {
1311 OBJ_RELEASE(zo_opt);
1312 }
1313 if (zo_ser) {
1314 OBJ_RELEASE(zo_ser);
1315 }
1316 }
1317
1318 typedef struct php_ion_unserializer_php {
1319 php_ion_unserializer unserializer;
1320 zend_object *opt, std;
1321 } php_ion_unserializer_php;
1322
1323 static inline void php_ion_unserializer_php_ctor(php_ion_unserializer_php *ser_obj)
1324 {
1325 php_ion_unserializer *global_ser = &php_ion_globals.unserializer;
1326 ser_obj->unserializer.ids = global_ser->ids;
1327 ser_obj->unserializer.tmp = global_ser->tmp;
1328 ser_obj->unserializer.addref = global_ser->addref;
1329
1330 zend_update_property_bool(ser_obj->std.ce, &ser_obj->std, ZEND_STRL("callMagicSerialize"),
1331 ser_obj->unserializer.call_magic);
1332 if (ser_obj->unserializer.call_custom) {
1333 zend_update_property_str(ser_obj->std.ce, &ser_obj->std, ZEND_STRL("callCustomSerialize"),
1334 ser_obj->unserializer.call_custom);
1335 ser_obj->unserializer.call_custom = zend_string_tolower(ser_obj->unserializer.call_custom);
1336 } else {
1337 zend_update_property_null(ser_obj->std.ce, &ser_obj->std, ZEND_STRL("callCustomSerialize"));
1338 }
1339 if (ser_obj->opt) {
1340 php_ion_reader_options *o_roptions = php_ion_obj(reader_options, ser_obj->opt);
1341 ser_obj->unserializer.options = &o_roptions->opt;
1342 update_property_obj(&ser_obj->std, ZEND_STRL("readerOptions"), ser_obj->opt);
1343 } else {
1344 zend_update_property_null(ser_obj->std.ce, &ser_obj->std, ZEND_STRL("readerOptions"));
1345 }
1346 }
1347
1348 static inline void php_ion_unserializer_php_dtor(php_ion_unserializer_php *obj)
1349 {
1350 if (obj->unserializer.call_custom) {
1351 zend_string_release(obj->unserializer.call_custom);
1352 }
1353 }
1354
1355 static inline void php_ion_unserialize_zval(php_ion_unserializer *ser, zval *return_value, ION_TYPE *typ);
1356
1357 static inline bool can_call_magic_unserialize(php_ion_unserializer *ser, zend_class_entry *ce)
1358 {
1359 return (ce && ce->__unserialize && ser->call_magic);
1360 }
1361
1362 static inline bool can_call_iface_unserialize(php_ion_unserializer *ser, zend_class_entry *ce)
1363 {
1364 return (ce && ce->unserialize);
1365 }
1366
1367 static inline bool can_call_custom_unserialize(php_ion_unserializer *ser, zend_object *zobject, zend_function **fn)
1368 {
1369 if (ser->call_custom) {
1370 return !!(*fn = zend_hash_find_ptr(&zobject->ce->function_table, ser->call_custom));
1371 }
1372 return false;
1373 }
1374
1375 static inline zval *php_ion_unserialize_class(php_ion_unserializer *ser, zval *return_value)
1376 {
1377 zend_class_entry *ce = zend_lookup_class(ser->annotations.object_class);
1378
1379 if (ce) {
1380 object_init_ex(return_value, ce);
1381 return zend_hash_next_index_insert(ser->ids, return_value);
1382 }
1383
1384 zend_throw_exception_ex(spl_ce_RuntimeException, IERR_IMPORT_NOT_FOUND,
1385 "Could not find class %s", ser->annotations.object_class->val);
1386 return NULL;
1387 }
1388
1389 static inline void php_ion_unserialize_object_enum(php_ion_unserializer *ser, zval *return_value)
1390 {
1391 zend_string *zs_case = zval_get_string(return_value);
1392 ION_CATCH();
1393
1394 zend_class_entry *ce = zend_lookup_class(ser->annotations.object_class);
1395 if (!ce || !(ce->ce_flags & ZEND_ACC_ENUM)) {
1396 zend_throw_exception_ex(spl_ce_RuntimeException, IERR_INVALID_TOKEN,
1397 "Not a valid enum: %s", ser->annotations.object_class->val);
1398 return;
1399 }
1400 if (!zend_hash_exists(CE_CONSTANTS_TABLE(ce), zs_case)) {
1401 zend_throw_exception_ex(spl_ce_RuntimeException, IERR_INVALID_TOKEN,
1402 "Not a valid enum case: %s::%s", ser->annotations.object_class->val, zs_case->val);
1403 return;
1404 }
1405 RETVAL_OBJ_COPY(zend_enum_get_case(ce, zs_case));
1406 zend_hash_next_index_insert(ser->ids, return_value);
1407 zend_string_release(zs_case);
1408 }
1409
1410 static inline void php_ion_unserialize_object_iface(php_ion_unserializer *ser, zval *return_value)
1411 {
1412 zend_class_entry *ce = zend_lookup_class(ser->annotations.object_class);
1413 if (can_call_iface_unserialize(ser, ce)) {
1414 zend_string *s = zval_get_string(return_value);
1415 ZVAL_NULL(return_value);
1416 zval *backref = zend_hash_next_index_insert(ser->ids, return_value);
1417 if (SUCCESS == ce->unserialize(backref, ce, (BYTE *) s->val, s->len, NULL)) {
1418 RETVAL_ZVAL(backref, 0, 0);
1419 } else if (!EG(exception)) {
1420 zend_throw_exception_ex(spl_ce_UnexpectedValueException, IERR_INTERNAL_ERROR,
1421 "Failed to unserialize class %s", ce->name->val);
1422 }
1423 zend_string_release(s);
1424 } else {
1425 zend_throw_exception_ex(spl_ce_RuntimeException, IERR_INVALID_TOKEN,
1426 "Class %s does not implement Serializable", ser->annotations.object_class->val);
1427 }
1428 }
1429
1430 static inline void php_ion_unserialize_field_name(php_ion_unserializer *ser, zend_string **key)
1431 {
1432 // FIXME: symbol table
1433 ION_STRING name;
1434 ION_CHECK(ion_reader_get_field_name(ser->reader, &name));
1435 if (!name.length) {
1436 ION_SYMBOL *is_ptr;
1437 ION_CHECK(ion_reader_get_field_name_symbol(ser->reader, &is_ptr));
1438 if (!ION_SYMBOL_IS_NULL(is_ptr) && is_ptr->value.length) {
1439 name = is_ptr->value;
1440 } else if (is_ptr) {
1441 char buf[MAX_LENGTH_OF_LONG + 1 + 1] = {0}, *end = buf + sizeof(buf) - 1, *ptr;
1442 ptr = zend_print_long_to_buf(end, is_ptr->sid);
1443 *--ptr = '$';
1444 *key = zend_string_init(ptr, end - ptr, 0);
1445 return;
1446 }
1447 }
1448 *key = zend_string_from_ion(&name);
1449 }
1450
1451 static void php_ion_unserialize_props(php_ion_unserializer *ser, zval *return_value)
1452 {
1453 zend_hash_next_index_insert(ser->ids, return_value);
1454
1455 ION_CHECK(ion_reader_step_in(ser->reader));
1456
1457 while (true) {
1458 ION_TYPE typ;
1459 ION_CHECK(ion_reader_next(ser->reader, &typ));
1460
1461 if (typ == tid_EOF) {
1462 break;
1463 }
1464
1465 zend_string *key;
1466 php_ion_unserialize_field_name(ser, &key);
1467 ION_CATCH();
1468
1469 zval zvalue;
1470 php_ion_unserialize_zval(ser, &zvalue, &typ);
1471 ION_CATCH(zend_string_release(key));
1472
1473 zend_class_entry *ce = Z_OBJCE_P(return_value);
1474 if (ser->annotations.object_prop && ser->annotations.property_class->val[0] != '*') {
1475 ce = zend_lookup_class(ser->annotations.property_class);
1476 }
1477 zend_update_property_ex(ce, Z_OBJ_P(return_value), key, &zvalue);
1478 zval_ptr_dtor(&zvalue);
1479 zend_string_release(key);
1480 }
1481
1482 ION_CHECK(ion_reader_step_out(ser->reader));
1483 }
1484
1485 static inline void php_ion_unserialize_hash(php_ion_unserializer *ser, zval *return_value)
1486 {
1487 zend_hash_next_index_insert(ser->ids, return_value);
1488
1489 ION_CHECK(ion_reader_step_in(ser->reader));
1490
1491 while (true) {
1492 ION_TYPE typ;
1493 ION_CHECK(ion_reader_next(ser->reader, &typ));
1494
1495 if (typ == tid_EOF) {
1496 break;
1497 }
1498
1499 zend_string *key;
1500 php_ion_unserialize_field_name(ser, &key);
1501 ION_CATCH();
1502
1503 zval zvalue;
1504 php_ion_unserialize_zval(ser, &zvalue, &typ);
1505 ION_CATCH(zend_string_release(key));
1506
1507 zend_symtable_update(HASH_OF(return_value), key, &zvalue);
1508
1509 zend_string_release(key);
1510 }
1511
1512 ION_CHECK(ion_reader_step_out(ser->reader));
1513 }
1514
1515 static inline void verify_unserializer(php_ion_unserializer *ser, zend_object *zobject, zend_function **fn)
1516 {
1517 switch (ser->annotations.object_type) {
1518 case 'c':
1519 *fn = NULL;
1520 break;
1521
1522 case 'C':
1523 if (!can_call_custom_unserialize(ser, zobject, fn)) {
1524 zend_throw_exception_ex(spl_ce_RuntimeException, IERR_INVALID_TOKEN,
1525 "Could not find custom serializer method of %s", ser->annotations.object_class->val);
1526 }
1527 break;
1528
1529 case 'O':
1530 if (!can_call_magic_unserialize(ser, zobject->ce)) {
1531 zend_throw_exception_ex(spl_ce_RuntimeException, IERR_INVALID_TOKEN,
1532 "Could not find method %s::__unserialize()", ser->annotations.object_class->val);
1533 }
1534 *fn = zobject->ce->__unserialize;
1535 break;
1536
1537 default:
1538 zend_throw_exception_ex(spl_ce_RuntimeException, IERR_INVALID_TOKEN,
1539 "Invalid object type %c", ser->annotations.object_type);
1540 }
1541 }
1542
1543 static inline void php_ion_unserialize_object(php_ion_unserializer *ser, zval *return_value)
1544 {
1545 // backup possible backref to array returned by magic/custom __serialize()
1546 zval *input = zend_hash_next_index_insert(ser->tmp, return_value);
1547
1548 php_ion_unserialize_class(ser, return_value);
1549 ION_CATCH();
1550
1551 zend_function *fn = NULL;
1552 zend_object *zobject = Z_OBJ_P(return_value);
1553 verify_unserializer(ser, zobject, &fn);
1554 ION_CATCH();
1555
1556 // plain object
1557 if (!fn) {
1558 php_ion_unserialize_props(ser, return_value);
1559 return;
1560 }
1561
1562 // magic object
1563 if (Z_TYPE_P(input) != IS_ARRAY) {
1564 zval_ptr_dtor(input);
1565 array_init(input);
1566 zend_hash_real_init_mixed(Z_ARRVAL_P(input));
1567 php_ion_unserialize_hash(ser, input);
1568 ION_CATCH();
1569 }
1570 zval rv;
1571 ZVAL_NULL(&rv);
1572 zend_call_method_with_1_params(zobject, zobject->ce, &fn, "", &rv, input);
1573 zval_ptr_dtor(&rv);
1574 }
1575
1576 static inline void php_ion_unserialize_struct(php_ion_unserializer *ser, zval *return_value)
1577 {
1578 if (ser->annotations.object_class) {
1579 switch (ser->annotations.object_type) {
1580 case 'S':
1581 php_ion_unserialize_object_iface(ser, return_value);
1582 break;
1583 case 'E':
1584 php_ion_unserialize_object_enum(ser, return_value);
1585 break;
1586 default:
1587 php_ion_unserialize_object(ser, return_value);
1588 }
1589 } else if (!ser->annotations.object_type) {
1590 array_init(return_value);
1591 php_ion_unserialize_hash(ser, return_value);
1592 } else if (ser->annotations.object_type == 'o') {
1593 object_init(return_value);
1594 php_ion_unserialize_hash(ser, return_value);
1595 } else {
1596 zend_throw_exception_ex(spl_ce_RuntimeException, IERR_INVALID_TOKEN,
1597 "Invalid object annotation %c::", ser->annotations.object_type);
1598 }
1599 }
1600
1601 static inline void php_ion_unserialize_list(php_ion_unserializer *ser, zval *return_value)
1602 {
1603 ION_CHECK(ion_reader_step_in(ser->reader));
1604 array_init(return_value);
1605 zend_hash_next_index_insert(ser->ids, return_value);
1606
1607 while (true) {
1608 ION_TYPE typ;
1609 ION_CHECK(ion_reader_next(ser->reader, &typ));
1610
1611 if (typ == tid_EOF) {
1612 break;
1613 }
1614
1615 zval next;
1616 php_ion_unserialize_zval(ser, &next, &typ);
1617 ION_CATCH();
1618
1619 zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &next);
1620 }
1621
1622 ION_CHECK(ion_reader_step_out(ser->reader));
1623 }
1624
1625 static inline void php_ion_reader_read_lob(ION_READER *reader, zval *return_value)
1626 {
1627 zend_string *zstr = zend_string_alloc(0x1000, 0);
1628 again:
1629 SIZE read = 0;
1630 iERR err = ion_reader_read_lob_bytes(reader, (BYTE *) zstr->val, zstr->len, &read);
1631 if (err == IERR_BUFFER_TOO_SMALL) {
1632 zstr = zend_string_extend(zstr, zstr->len << 2, 0);
1633 goto again;
1634 }
1635 ION_CHECK(err, zend_string_release(zstr));
1636 if (zstr->len > read) {
1637 zstr->val[read] = 0;
1638 zstr = zend_string_truncate(zstr, read, 0);
1639 }
1640 RETURN_STR(zstr);
1641 }
1642
1643 static inline void php_ion_reader_read_timestamp(ION_READER *reader, ION_READER_OPTIONS *opt, zval *return_value)
1644 {
1645 ION_TIMESTAMP ts;
1646 ION_CHECK(ion_reader_read_timestamp(reader, &ts));
1647
1648 object_init_ex(return_value, ce_Timestamp);
1649 php_ion_timestamp *ts_obj = php_ion_obj(timestamp, Z_OBJ_P(return_value));
1650
1651 zend_string *fmt = NULL;
1652 decContext *ctx = opt ? opt->decimal_context : NULL;
1653 ts_obj->time = php_time_from_ion(&ts, ctx, &fmt);
1654 php_ion_timestamp_ctor(ts_obj, ts.precision, fmt, NULL, NULL);
1655 zend_string_release(fmt);
1656
1657 OBJ_CHECK(ts_obj);
1658 }
1659
1660 static inline void php_ion_reader_read_int(ION_READER *reader, zval *return_value)
1661 {
1662 ION_INT *num = NULL;
1663 ION_CHECK(ion_int_alloc(reader, &num));
1664 ION_CHECK(ion_reader_read_ion_int(reader, num));
1665
1666 // TODO: SIZEOF_ZEND_LONG == 4
1667 int64_t i64;
1668 iERR err = ion_int_to_int64(num, &i64);
1669 switch (err) {
1670 case IERR_OK:
1671 RETVAL_LONG(i64);
1672 goto done;
1673
1674 case IERR_NUMERIC_OVERFLOW:
1675 SIZE max, len;
1676 ION_CHECK(ion_int_char_length(num, &max));
1677 zend_string *zs = zend_string_alloc(max, 0);
1678
1679 err = ion_int_to_char(num, (BYTE *) zs->val, max, &len);
1680 zs->val[zs->len = len] = 0;
1681 RETVAL_STR(zs);
1682 /* fall through */
1683
1684 default:
1685 done:
1686 ion_int_free(num);
1687 ION_CHECK(err);
1688 }
1689 }
1690
1691 static inline void php_ion_unserialize_backref(php_ion_unserializer *ser, zval *return_value)
1692 {
1693 zval *backref = zend_hash_index_find(ser->ids, Z_LVAL_P(return_value));
1694
1695 if (backref) {
1696 ZVAL_COPY_VALUE(return_value, backref);
1697 zend_hash_next_index_insert(ser->addref, return_value);
1698 } else {
1699 zend_throw_exception_ex(spl_ce_RuntimeException, IERR_INTERNAL_ERROR,
1700 "Could not find back reference %ld", Z_LVAL_P(return_value));
1701 }
1702 }
1703
1704 static inline void php_ion_unserialize_annotations(php_ion_unserializer *ser)
1705 {
1706 memset(&ser->annotations, 0, sizeof(ser->annotations));
1707
1708 int32_t ann_cnt;
1709 ION_CHECK(ion_reader_get_annotation_count(ser->reader, &ann_cnt));
1710 for (int32_t i = 0; i < ann_cnt; ++i) {
1711 ION_STRING ann_str;
1712 ION_CHECK(ion_reader_get_an_annotation(ser->reader, i, &ann_str));
1713
1714 if (ann_str.length != 1) {
1715 continue;
1716 }
1717
1718 switch (*ann_str.value) {
1719 case 'R':
1720 if (ser->annotations.makeref) {
1721 zend_throw_exception_ex(spl_ce_RuntimeException, IERR_INVALID_SYNTAX,
1722 "Invalid multiple reference annotations");
1723 return;
1724 }
1725 ser->annotations.makeref = true;
1726 break;
1727
1728 case 'r':
1729 if (ser->annotations.backref) {
1730 zend_throw_exception_ex(spl_ce_RuntimeException, IERR_INVALID_SYNTAX,
1731 "Invalid multiple back reference annotations");
1732 return;
1733 }
1734 ser->annotations.backref = true;
1735 break;
1736
1737 case 'p':
1738 if (ser->annotations.object_prop) {
1739 zend_throw_exception_ex(spl_ce_RuntimeException, IERR_INVALID_SYNTAX,
1740 "Invalid multiple object property annotations");
1741 return;
1742 }
1743 ser->annotations.object_prop = true;
1744
1745 ION_STRING prop_class;
1746 ION_CHECK(ion_reader_get_an_annotation(ser->reader, ++i, &prop_class));
1747 ser->annotations.property_class = zend_string_from_ion(&prop_class);
1748
1749 zval zptmp;
1750 ZVAL_STR(&zptmp, ser->annotations.property_class);
1751 zend_hash_next_index_insert(ser->tmp, &zptmp);
1752 break;
1753
1754 case 'E':
1755 case 'S':
1756 case 'O':
1757 case 'C':
1758 case 'o':
1759 case 'c':
1760 if (ser->annotations.object_type) {
1761 zend_throw_exception_ex(spl_ce_RuntimeException, IERR_INVALID_SYNTAX,
1762 "Invalid multiple object type annotations: %c::%c",
1763 ser->annotations.object_type, *ann_str.value);
1764 return;
1765 }
1766 if ('o' != (ser->annotations.object_type = *ann_str.value)) {
1767 ION_STRING class_name;
1768 ION_CHECK(ion_reader_get_an_annotation(ser->reader, ++i, &class_name));
1769 ser->annotations.object_class = zend_string_from_ion(&class_name);
1770
1771 zval zctmp;
1772 ZVAL_STR(&zctmp, ser->annotations.object_class);
1773 zend_hash_next_index_insert(ser->tmp, &zctmp);
1774 }
1775 break;
1776 }
1777
1778 // sanity checks
1779 if (ser->annotations.object_type && ser->annotations.object_type != 'o' && !ser->annotations.object_class) {
1780 zend_throw_exception_ex(spl_ce_RuntimeException, IERR_INVALID_SYNTAX,
1781 "Invalid object annotation without class name: %c::", ser->annotations.object_type);
1782 return;
1783 }
1784 if (ser->annotations.object_type == 'o' && ser->annotations.object_class) {
1785 zend_throw_exception_ex(spl_ce_RuntimeException, IERR_INVALID_SYNTAX,
1786 "Invalid object annotation with class name: o::%s", ser->annotations.object_class->val);
1787 return;
1788 }
1789 }
1790 }
1791
1792 static inline void php_ion_unserialize_zval(php_ion_unserializer *ser, zval *return_value, ION_TYPE *typ)
1793 {
1794 if (typ) {
1795 memcpy(&ser->type, typ, sizeof(ser->type));
1796 } else {
1797 typ = &ser->type;
1798 ION_CHECK(ion_reader_next(ser->reader, typ));
1799 }
1800
1801 php_ion_unserialize_annotations(ser);
1802 ION_CATCH();
1803
1804 if (ser->annotations.makeref) {
1805 ZVAL_MAKE_REF(return_value);
1806 zend_hash_next_index_insert(ser->ids, return_value);
1807 ZVAL_DEREF(return_value);
1808 }
1809
1810 if (ION_TYPE_INT(*typ) > 0) {
1811 BOOL is_null;
1812 ION_CHECK(ion_reader_is_null(ser->reader, &is_null));
1813 if (is_null) {
1814 RETURN_NULL();
1815 }
1816 }
1817
1818 switch (ION_TYPE_INT(*typ)) {
1819 case tid_NULL_INT:
1820 ION_CHECK(ion_reader_read_null(ser->reader, typ));
1821 RETURN_NULL();
1822
1823 case tid_BOOL_INT:
1824 BOOL bval;
1825 ION_CHECK(ion_reader_read_bool(ser->reader, &bval));
1826 RETURN_BOOL(bval);
1827
1828 case tid_INT_INT:
1829 php_ion_reader_read_int(ser->reader, return_value);
1830 if (ser->annotations.backref) {
1831 ION_CATCH();
1832 php_ion_unserialize_backref(ser, return_value);
1833 }
1834 if (ser->annotations.object_type) {
1835 if (!ser->annotations.backref) {
1836 zend_throw_exception_ex(spl_ce_RuntimeException, IERR_INVALID_SYNTAX,
1837 "Invalid object type annotation: %c::" ZEND_LONG_FMT,
1838 ser->annotations.object_type, Z_LVAL_P(return_value));
1839 return;
1840 }
1841 goto unserialize_struct;
1842 }
1843 return;
1844
1845 case tid_FLOAT_INT:
1846 double d;
1847 ION_CHECK(ion_reader_read_double(ser->reader, &d));
1848 RETURN_DOUBLE(d);
1849
1850 case tid_DECIMAL_INT:
1851 object_init_ex(return_value, ce_Decimal);
1852 php_ion_decimal *dec = php_ion_obj(decimal, Z_OBJ_P(return_value));
1853 ION_CHECK(ion_reader_read_ion_decimal(ser->reader, &dec->dec));
1854 php_ion_decimal_ctor(dec);
1855 zend_hash_next_index_insert(ser->ids, return_value);
1856 return;
1857
1858 case tid_TIMESTAMP_INT:
1859 php_ion_reader_read_timestamp(ser->reader, ser->options, return_value);
1860 zend_hash_next_index_insert(ser->ids, return_value);
1861 return;
1862
1863 case tid_SYMBOL_INT:
1864 ION_SYMBOL sym;
1865 ION_CHECK(ion_reader_read_ion_symbol(ser->reader, &sym));
1866 php_ion_symbol_zval(&sym, return_value);
1867 if (ser->annotations.object_type) {
1868 zend_hash_next_index_insert(ser->tmp, return_value);
1869 goto unserialize_struct;
1870 }
1871 zend_hash_next_index_insert(ser->ids, return_value);
1872 return;
1873
1874 case tid_STRING_INT:
1875 ION_STRING str;
1876 ION_CHECK(ion_reader_read_string(ser->reader, &str));
1877 RETVAL_STRINGL((char *) str.value, str.length);
1878 if (ser->annotations.object_type) {
1879 zend_hash_next_index_insert(ser->tmp, return_value);
1880 goto unserialize_struct;
1881 }
1882 zend_hash_next_index_insert(ser->ids, return_value);
1883 return;
1884
1885 case tid_CLOB_INT:
1886 case tid_BLOB_INT:
1887 php_ion_reader_read_lob(ser->reader, return_value);
1888 if (ser->annotations.object_type) {
1889 zend_hash_next_index_insert(ser->tmp, return_value);
1890 goto unserialize_struct;
1891 }
1892 zend_hash_next_index_insert(ser->ids, return_value);
1893 return;
1894
1895 case tid_LIST_INT:
1896 case tid_SEXP_INT: // FIXME
1897 php_ion_unserialize_list(ser, return_value);
1898 if (!ser->annotations.object_type) {
1899 return;
1900 }
1901 /* fall through */
1902
1903 case tid_STRUCT_INT:
1904 unserialize_struct: ;
1905 php_ion_unserialize_struct(ser, return_value);
1906 return;
1907
1908 case tid_none_INT:
1909 ZEND_ASSERT(0);
1910 break;
1911
1912 case tid_DATAGRAM_INT:
1913 ZEND_ASSERT(!"datagram");
1914 case tid_EOF_INT:
1915 return;
1916 }
1917 }
1918
1919 php_ion_decl(unserializer_php, Unserializer_PHP, php_ion_unserializer_php_dtor(obj));
1920
1921 static inline void php_ion_unserialize_ex(php_ion_unserializer *ser, zval *return_value)
1922 {
1923 if (ser->multi_seq) {
1924 array_init(return_value);
1925 }
1926
1927 do {
1928 zval tmp;
1929 ZVAL_NULL(&tmp);
1930 php_ion_globals_unserializer_step();
1931 php_ion_unserialize_zval(ser, &tmp, NULL);
1932 php_ion_globals_unserializer_exit();
1933 ION_CATCH(zval_ptr_dtor(&tmp));
1934
1935 if (!ser->multi_seq) {
1936 RETURN_COPY_VALUE(&tmp);
1937 } else if (ser->type != tid_EOF) {
1938 zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &tmp);
1939 }
1940 } while (ser->type != tid_EOF);
1941 }
1942
1943 void php_ion_unserialize(php_ion_unserializer *ser, zval *zdata, zval *return_value)
1944 {
1945 zend_object *zo_opt = NULL, *zo_ser = NULL;
1946
1947 if (!ser) {
1948 zo_ser = create_ion_Unserializer_PHP(NULL);
1949 php_ion_unserializer_php *o_ser = php_ion_obj(unserializer_php, zo_ser);
1950 PTR_CHECK(o_ser);
1951 o_ser->unserializer.call_magic = true;
1952 php_ion_unserializer_php_ctor(o_ser);
1953 ION_CATCH();
1954 ser = &o_ser->unserializer;
1955 }
1956
1957 zend_object *zo_reader;
1958 php_ion_reader *reader;
1959 ZVAL_DEREF(zdata);
1960
1961 if (Z_TYPE_P(zdata) == IS_RESOURCE) {
1962 zo_reader = create_ion_Reader_Reader(ce_Reader_Stream_Reader);
1963 reader = php_ion_obj(reader, zo_reader);
1964 reader->type = STREAM_READER;
1965 php_stream_from_zval_no_verify(reader->stream.ptr, zdata);
1966 } else if (Z_TYPE_P(zdata) <= IS_STRING) {
1967 zo_reader = create_ion_Reader_Reader(ce_Reader_Buffer_Reader);
1968 reader = php_ion_obj(reader, zo_reader);
1969 reader->type = BUFFER_READER;
1970 reader->buffer = zval_get_string(zdata);
1971 } else {
1972 zend_throw_exception_ex(spl_ce_InvalidArgumentException, IERR_INVALID_ARG,
1973 "Invalid source to unserialize; expected string or resource");
1974 if (zo_ser) {
1975 OBJ_RELEASE(zo_ser);
1976 }
1977 return;
1978 }
1979
1980 if (ser->options) {
1981 zo_opt = reader->opt = create_ion_Reader_Options(NULL);
1982 php_ion_obj(reader_options, reader->opt)->opt = *ser->options;
1983 }
1984
1985 php_ion_reader_ctor(reader);
1986 ser->reader = reader->reader;
1987
1988 if (!EG(exception)) {
1989 php_ion_unserialize_ex(ser, return_value);
1990 }
1991
1992 OBJ_RELEASE(zo_reader);
1993 if (zo_opt) {
1994 OBJ_RELEASE(zo_opt);
1995 }
1996 if (zo_ser) {
1997 OBJ_RELEASE(zo_ser);
1998 }
1999 }