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