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