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