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