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