From: Michael Wallner Date: Mon, 6 Dec 2021 20:27:40 +0000 (+0100) Subject: initial ci X-Git-Tag: v0.1.0~99 X-Git-Url: https://git.m6w6.name/?a=commitdiff_plain;h=14a010b784f6228a293c90502c46a7143d89afbf;p=awesomized%2Fext-ion initial ci --- 14a010b784f6228a293c90502c46a7143d89afbf diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..7105c46 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,27 @@ +; see http://editorconfig.org +root = true + +[*] +end_of_line = lf +insert_final_newline = true +indent_style = tab +charset = utf-8 +trim_trailing_whitespace = true + +[*.php] +indent_style = space +indent_size = 4 + +[*.md] +trim_trailing_whitespace = false + +[*.json] +indent_style = space +indent_size = 4 + +[package.xml] +indent_style = space +indent_size = 1 + +[config.w32] +end_of_line = crlf diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ae434fe --- /dev/null +++ b/.gitignore @@ -0,0 +1,41 @@ +*.lo +*.la +.libs +acinclude.m4 +aclocal.m4 +autom4te.cache +build +config.guess +config.h +config.h.in +config.log +config.nice +config.status +config.sub +configure +configure.ac +configure.in +include +install-sh +libtool +ltmain.sh +Makefile +Makefile.fragments +Makefile.global +Makefile.objects +missing +mkinstalldirs +modules +php_test_results_*.txt +phpt.* +run-test-info.php +run-tests.php +tests/**/*.diff +tests/**/*.out +tests/**/*.php +tests/**/*.exp +tests/**/*.log +tests/**/*.sh +tests/**/*.db +tests/**/*.mem +tmp-php.ini diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..67bbd91 --- /dev/null +++ b/AUTHORS @@ -0,0 +1 @@ +Michael Wallner diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..968bd44 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,39 @@ +# Contributor Code of Conduct + +As contributors and maintainers of this project, and in the interest of +fostering an open and welcoming community, we pledge to respect all people who +contribute through reporting issues, posting feature requests, updating +documentation, submitting pull requests or patches, and other activities. + +We are committed to making participation in this project a harassment-free +experience for everyone, regardless of level of experience, gender, gender +identity and expression, sexual orientation, disability, personal appearance, +body size, race, ethnicity, age, religion, or nationality. + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery +* Personal attacks +* Trolling or insulting/derogatory comments +* Public or private harassment +* Publishing other's private information, such as physical or electronic + addresses, without explicit permission +* Other unethical or unprofessional conduct. + +Project maintainers have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct. By adopting this Code of Conduct, project +maintainers commit themselves to fairly and consistently applying these +principles to every aspect of managing this project. Project maintainers who do +not follow or enforce the Code of Conduct may be permanently removed from the +project team. + +This code of conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by opening an issue or contacting one or more of the project maintainers. + +This Code of Conduct is adapted from the +[Contributor Covenant](http://contributor-covenant.org), version 1.2.0, +available at http://contributor-covenant.org/version/1/2/0/. diff --git a/CREDITS b/CREDITS new file mode 100644 index 0000000..e0f497d --- /dev/null +++ b/CREDITS @@ -0,0 +1,2 @@ +ion +Michael Wallner \ No newline at end of file diff --git a/EXPERIMENTAL b/EXPERIMENTAL new file mode 100644 index 0000000..e69de29 diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..2cb49b2 --- /dev/null +++ b/LICENSE @@ -0,0 +1,22 @@ +Copyright (c) 2021, Michael Wallner . +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/config.m4 b/config.m4 new file mode 100644 index 0000000..4d85c67 --- /dev/null +++ b/config.m4 @@ -0,0 +1,56 @@ + +PHP_ARG_WITH([ion], + [for ion support], + [AS_HELP_STRING([--with-ion], + [path to libion])], + [no]) + +if test "$PHP_ION" != "no"; then + + SEARCH_PATH="/usr/local /usr" # you might want to change this + SEARCH_FOR="/include/ionc/ion.h" # you most likely want to change this + if test -r $PHP_ION/$SEARCH_FOR; then # path given as parameter + ION_DIR=$PHP_ION + else # search default path list + AC_MSG_CHECKING([for ion files in default path]) + for i in $SEARCH_PATH ; do + if test -r $i/$SEARCH_FOR; then + ION_DIR=$i + AC_MSG_RESULT(found in $i) + fi + done + fi + dnl + if test -z "$ION_DIR"; then + AC_MSG_RESULT([not found]) + AC_MSG_ERROR([Please reinstall the ion distribution]) + fi + + PHP_ADD_INCLUDE($ION_DIR/include) + + LIBNAME=ionc + LIBSYMBOL=ion_error_to_str + PHP_CHECK_LIBRARY($LIBNAME, $LIBSYMBOL, + [ + PHP_ADD_LIBRARY_WITH_PATH($LIBNAME, $ION_DIR/$PHP_LIBDIR, ION_SHARED_LIBADD) + ],[ + AC_MSG_ERROR([could not link with ionc library.]) + ],[ + -L$ION_DIR/$PHP_LIBDIR -lm + ]) + LIBNAME=decNumber + LIBSYMBOL=decQuadFromInt32 + PHP_CHECK_LIBRARY($LIBNAME, $LIBSYMBOL, + [ + PHP_ADD_LIBRARY_WITH_PATH($LIBNAME, $ION_DIR/$PHP_LIBDIR, ION_SHARED_LIBADD) + ],[ + AC_MSG_ERROR([could not link with ionc library.]) + ],[ + -L$ION_DIR/$PHP_LIBDIR -lm + ]) + + PHP_SUBST(ION_SHARED_LIBADD) + AC_DEFINE(HAVE_ION, 1, [ Have ion support ]) + + PHP_NEW_EXTENSION(ion, ion.c, $ext_shared) +fi diff --git a/gen_stub.php.diff b/gen_stub.php.diff new file mode 100644 index 0000000..d01ec3d --- /dev/null +++ b/gen_stub.php.diff @@ -0,0 +1,90 @@ +diff --git a/build/gen_stub.php b/build/gen_stub.php +index 5f74d26dbc3..01a19988161 100755 +--- a/build/gen_stub.php ++++ b/build/gen_stub.php +@@ -793,7 +793,7 @@ class FunctionName implements FunctionOrMethodName { + } + + public function getDeclarationName(): string { +- return $this->name->getLast(); ++ return strtr($this->name->toString(), "\\", "_"); + } + + public function getDeclaration(): string { +@@ -1152,8 +1152,8 @@ class FuncInfo { + if ($namespace) { + // Render A\B as "A\\B" in C strings for namespaces + return sprintf( +- "\tZEND_NS_FE(\"%s\", %s, %s)\n", +- addslashes($namespace), $declarationName, $this->getArgInfoName()); ++ "\tZEND_NS_RAW_FENTRY(\"%s\", \"%s\", ZEND_FN(%s), %s, 0)\n", ++ addslashes($namespace), substr((string)$this->name, strlen($namespace)+1), $declarationName, $this->getArgInfoName()); + } else { + return sprintf("\tZEND_FE(%s, %s)\n", $declarationName, $this->getArgInfoName()); + } +@@ -1608,7 +1608,7 @@ class EnumCaseInfo { + public function getDeclaration(): string { + $escapedName = addslashes($this->name); + if ($this->value === null) { +- $code = "\n\tzend_enum_add_case_cstr(class_entry, \"$escapedName\", NULL);\n"; ++ $code = "\tzend_enum_add_case_cstr(class_entry, \"$escapedName\", NULL);\n"; + } else { + $evaluator = new ConstExprEvaluator(function (Expr $expr) { + throw new Exception("Enum case $this->name has an unsupported value"); +@@ -2369,7 +2369,7 @@ function parseFunctionLike( + function parseProperty( + Name $class, + int $flags, +- Stmt\PropertyProperty $property, ++ Stmt\PropertyProperty|Node\Param $property, + ?Node $type, + ?DocComment $comment, + PrettyPrinterAbstract $prettyPrinter +@@ -2404,13 +2404,23 @@ function parseProperty( + } + } + ++ $default = $property->default; ++ if ($property instanceof Node\Param) { ++ $name = $property->var->name; ++ if ($property->flags & Stmt\Class_::MODIFIER_READONLY) { ++ $default = null; ++ } ++ } else { ++ $name = $property->name; ++ } ++ + return new PropertyInfo( +- new PropertyName($class, $property->name->__toString()), ++ new PropertyName($class, (string) $name), + $flags, + $propertyType, + $phpDocType ? Type::fromString($phpDocType) : null, +- $property->default, +- $property->default ? $prettyPrinter->prettyPrintExpr($property->default) : null, ++ $default, ++ $default ? $prettyPrinter->prettyPrintExpr($default) : null, + $isDocReadonly + ); + } +@@ -2594,6 +2604,20 @@ function handleStatements(FileInfo $fileInfo, array $stmts, PrettyPrinterAbstrac + $classStmt, + $cond + ); ++ if ($classStmt->name->toString() === "__construct") { ++ foreach ($classStmt->params as $param) { ++ if ($param->flags) { ++ $propertyInfos[] = parseProperty( ++ $className, ++ $param->flags, ++ $param, ++ $param->type, ++ $param->getDocComment(), ++ $prettyPrinter ++ ); ++ } ++ } ++ } + } else if ($classStmt instanceof Stmt\EnumCase) { + $enumCaseInfos[] = new EnumCaseInfo( + $classStmt->name->toString(), $classStmt->expr); diff --git a/ion.c b/ion.c new file mode 100644 index 0000000..a4915e3 --- /dev/null +++ b/ion.c @@ -0,0 +1,1536 @@ +/* + +--------------------------------------------------------------------+ + | PECL :: ion | + +--------------------------------------------------------------------+ + | Redistribution and use in source and binary forms, with or without | + | modification, are permitted provided that the conditions mentioned | + | in the accompanying LICENSE file are met. | + +--------------------------------------------------------------------+ + | Copyright (c) 2021, Michael Wallner | + +--------------------------------------------------------------------+ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "php.h" +#include "ext/standard/info.h" + +#include "Zend/zend_enum.h" +#include "Zend/zend_exceptions.h" +#include "Zend/zend_closures.h" +#include "Zend/zend_interfaces.h" +#include "Zend/zend_smart_str.h" + +#include "ext/date/php_date.h" +#include "ext/spl/spl_exceptions.h" +#include "ext/spl/spl_iterators.h" + +#define DECNUMDIGITS 34 /* DECQUAD_Pmax */ +#include "ionc/ion.h" + +#include "php_ion.h" +#define ZEND_ARG_VARIADIC_OBJ_TYPE_MASK(pass_by_ref, name, classname, type_mask, default_value) \ + { #name, ZEND_TYPE_INIT_CLASS_CONST_MASK(#classname, type_mask | _ZEND_ARG_INFO_FLAGS(pass_by_ref, 1, 0)), default_value }, +#include "ion_arginfo.h" +#include "ion_private.h" + +ZEND_METHOD(ion_Symbol_ImportLocation, __construct) +{ + zend_long location; + php_ion_symbol_iloc *obj = php_ion_obj(symbol_iloc, Z_OBJ_P(ZEND_THIS)); + + PTR_CHECK(obj); + + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_STR(obj->name) + Z_PARAM_LONG(location) + ZEND_PARSE_PARAMETERS_END(); + + obj->loc.location = location; + php_ion_symbol_iloc_ctor(obj); +} +ZEND_METHOD(ion_Symbol, __construct) +{ + zend_long sid = -1; + php_ion_symbol *obj = php_ion_obj(symbol, Z_OBJ_P(ZEND_THIS)); + + PTR_CHECK(obj); + + ZEND_PARSE_PARAMETERS_START(1, 3) + Z_PARAM_STR(obj->value) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(sid) + Z_PARAM_OBJ_OF_CLASS_OR_NULL(obj->iloc, ce_Symbol_ImportLocation) + ZEND_PARSE_PARAMETERS_END(); + + obj->sym.sid = sid; + php_ion_symbol_ctor(obj); +} +ZEND_METHOD(ion_Symbol, equals) +{ + zend_object *other_obj; + php_ion_symbol *sym = php_ion_obj(symbol, Z_OBJ_P(ZEND_THIS)); + + OBJ_CHECK(sym); + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_OBJ_OF_CLASS(other_obj, ce_Symbol) + ZEND_PARSE_PARAMETERS_END(); + + BOOL eq = FALSE; + iERR err = ion_symbol_is_equal( + &sym->sym, + &php_ion_obj(symbol, other_obj)->sym, &eq); + ION_CHECK(err); + RETVAL_BOOL(eq); +} +ZEND_METHOD(ion_Timestamp, __construct) +{ + zend_long precision; + zend_string *fmt = NULL, *dt = NULL; + zval *tz = NULL; + php_ion_timestamp *obj = php_ion_obj(timestamp, Z_OBJ_P(ZEND_THIS)); + PTR_CHECK(obj); + + ZEND_PARSE_PARAMETERS_START(1, 4) + Z_PARAM_LONG(precision) + Z_PARAM_OPTIONAL + Z_PARAM_STR_OR_NULL(fmt) + Z_PARAM_STR(dt) + Z_PARAM_ZVAL(tz) + ZEND_PARSE_PARAMETERS_END(); + + php_ion_timestamp_ctor(obj, precision, fmt, dt, tz); +} +ZEND_METHOD(ion_Timestamp, __toString) +{ + php_ion_timestamp *obj = php_ion_obj(timestamp, Z_OBJ_P(ZEND_THIS)); + OBJ_CHECK(obj); + + ZEND_PARSE_PARAMETERS_NONE(); + + zval fmt; + ZVAL_NULL(&fmt); + zend_call_method_with_1_params(&obj->std, obj->std.ce, NULL, "format", return_value, + zend_read_property(obj->std.ce, &obj->std, ZEND_STRL("format"), 0, &fmt)); +} +ZEND_METHOD(ion_Decimal, __construct) +{ + php_ion_decimal *obj = php_ion_obj(decimal, Z_OBJ_P(ZEND_THIS)); + PTR_CHECK(obj); + + zend_long num; + zend_string *zstr; + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_STR_OR_LONG(zstr, num) + Z_PARAM_OPTIONAL + Z_PARAM_OBJ_OF_CLASS_OR_NULL(obj->ctx, ce_Decimal_Context) + ZEND_PARSE_PARAMETERS_END(); + + if (zstr) { + ION_CHECK(ion_decimal_from_string(&obj->dec, zstr->val, + obj->ctx ? &php_ion_obj(decimal_ctx, obj->ctx)->ctx : NULL)); + } else if (num <= INT32_MAX && num >= INT32_MIN) { + ION_CHECK(ion_decimal_from_int32(&obj->dec, num)); + } else if (num > 0 && num <= UINT32_MAX) { + ION_CHECK(ion_decimal_from_uint32(&obj->dec, num)); + } else { + zend_throw_exception_ex(spl_ce_OverflowException, 0, + "Integer value out of bounds: " ZEND_LONG_FMT " (INT32_MIN < n < UINT32_MAX)", num); + return; + } + + php_ion_decimal_ctor(obj); +} +ZEND_METHOD(ion_Decimal, equals) +{ + php_ion_decimal *obj = php_ion_obj(decimal, Z_OBJ_P(ZEND_THIS)); + PTR_CHECK(obj); + + zend_object *dec_obj; + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_OBJ_OF_CLASS(dec_obj, ce_Decimal) + ZEND_PARSE_PARAMETERS_END(); + + BOOL is = FALSE; + ION_CHECK(ion_decimal_equals(&obj->dec, &php_ion_obj(decimal, dec_obj)->dec, + obj->ctx ? &php_ion_obj(decimal_ctx, obj->ctx)->ctx : NULL, &is)); + RETURN_BOOL(is); +} +ZEND_METHOD(ion_Decimal, zero) +{ + php_ion_decimal *obj = php_ion_obj(decimal, Z_OBJ_P(ZEND_THIS)); + PTR_CHECK(obj); + + ZEND_PARSE_PARAMETERS_NONE(); + + ION_CHECK(ion_decimal_zero(&obj->dec)); +} +ZEND_METHOD(ion_Decimal, __toString) +{ + php_ion_decimal *obj = php_ion_obj(decimal, Z_OBJ_P(ZEND_THIS)); + PTR_CHECK(obj); + + ZEND_PARSE_PARAMETERS_NONE(); + + RETURN_STR(php_ion_decimal_to_string(&obj->dec)); +} +ZEND_METHOD(ion_Decimal, toInt) +{ + php_ion_decimal *obj = php_ion_obj(decimal, Z_OBJ_P(ZEND_THIS)); + PTR_CHECK(obj); + + ZEND_PARSE_PARAMETERS_NONE(); + + int32_t i32 = 0; + iERR err = ion_decimal_to_int32(&obj->dec, + obj->ctx ? &php_ion_obj(decimal_ctx, obj->ctx)->ctx : NULL, &i32); + if (IERR_OK == err) { + RETURN_LONG(i32); + } + if (!ion_decimal_is_negative(&obj->dec)) { + uint32_t u32 = 0; + err = ion_decimal_to_uint32(&obj->dec, + obj->ctx ? &php_ion_obj(decimal_ctx, obj->ctx)->ctx : NULL, &u32); + if (IERR_OK == err) { + RETURN_LONG(u32); + } + } + ION_CHECK(err); +} +ZEND_METHOD(ion_Reader_Options, __construct) +{ + php_ion_reader_options *opt = php_ion_obj(reader_options, Z_OBJ_P(ZEND_THIS)); + zend_bool ret_sys_val = false, skip_validation = false; + zend_long ch_nl = 0xa, max_depth = 10, max_ann, max_ann_buf = 512, + sym_thr = 0x1000, uval_thr = 0x1000, chunk_thr = 0x1000, alloc_pgsz = 0x1000; + + PTR_CHECK(opt); + + ZEND_PARSE_PARAMETERS_START(0, 13) + Z_PARAM_OPTIONAL + // public readonly ?\ion\Catalog $catalog = null, + Z_PARAM_OBJ_OF_CLASS_OR_NULL(opt->cat, ce_Catalog) + // public readonly ?\ion\Decimal\Context $decimalContext = null, + Z_PARAM_OBJ_OF_CLASS_OR_NULL(opt->dec_ctx, ce_Decimal_Context) + // public readonly ?\Closure $onContextChange = null, + Z_PARAM_OBJ_OF_CLASS_OR_NULL(opt->cb, zend_ce_closure); + // public readonly bool $returnSystemValues = false, + Z_PARAM_BOOL(ret_sys_val) + // public readonly int $newLine = 0xa, + Z_PARAM_LONG(ch_nl) + // public readonly int $maxContainerDepth = 10, + Z_PARAM_LONG(max_depth) + // public readonly int $maxAnnotations = 10, + Z_PARAM_LONG(max_ann) + // public readonly int $maxAnnotationBuffered = 512, + Z_PARAM_LONG(max_ann_buf) + // public readonly int $symbolThreshold = 4096, + Z_PARAM_LONG(sym_thr) + // public readonly int $userValueThreshold = 4096, + Z_PARAM_LONG(uval_thr) + // public readonly int $chunkThreshold = 4096, + Z_PARAM_LONG(chunk_thr) + // public readonly int $allocationPageSize = 4096, + Z_PARAM_LONG(alloc_pgsz) + // public readonly bool $skipCharacterValidation = false, + Z_PARAM_BOOL(skip_validation) + ZEND_PARSE_PARAMETERS_END(); + + opt->opt.context_change_notifier = EMPTY_READER_CHANGE_NOTIFIER; + if (opt->cb) { + update_property_obj(&opt->std, ZEND_STRL("onContextChange"), opt->cb); + } + if (opt->cat) { + update_property_obj(&opt->std, ZEND_STRL("catalog"), opt->cat); + opt->opt.pcatalog = php_ion_obj(catalog, opt->cat)->cat; + } + if (opt->dec_ctx) { + update_property_obj(&opt->std, ZEND_STRL("decimalContext"), opt->dec_ctx); + opt->opt.decimal_context = &php_ion_obj(decimal_ctx, opt->dec_ctx)->ctx; + } + zend_update_property_bool(opt->std.ce, &opt->std, ZEND_STRL("returnSystemValues"), + opt->opt.return_system_values = ret_sys_val); + zend_update_property_long(opt->std.ce, &opt->std, ZEND_STRL("newLine"), + opt->opt.new_line_char = ch_nl); + zend_update_property_long(opt->std.ce, &opt->std, ZEND_STRL("maxContainerDepth"), + opt->opt.max_container_depth = max_depth); + zend_update_property_long(opt->std.ce, &opt->std, ZEND_STRL("maxAnnotationCount"), + opt->opt.max_annotation_count = max_ann); + zend_update_property_long(opt->std.ce, &opt->std, ZEND_STRL("maxAnnotationBuffered"), + opt->opt.max_annotation_buffered = max_ann_buf); + zend_update_property_long(opt->std.ce, &opt->std, ZEND_STRL("symbolThreshold"), + opt->opt.symbol_threshold = sym_thr); + zend_update_property_long(opt->std.ce, &opt->std, ZEND_STRL("userValueThreshold"), + opt->opt.user_value_threshold = uval_thr); + zend_update_property_long(opt->std.ce, &opt->std, ZEND_STRL("chunkThreshold"), + opt->opt.chunk_threshold = chunk_thr); + zend_update_property_long(opt->std.ce, &opt->std, ZEND_STRL("allocationPageSize"), + opt->opt.allocation_page_size = alloc_pgsz); + zend_update_property_long(opt->std.ce, &opt->std, ZEND_STRL("skipCharacterValidation"), + opt->opt.skip_character_validation = skip_validation); +} +ZEND_METHOD(ion_Reader_Reader, hasChildren) +{ + php_ion_reader *obj = php_ion_obj(reader, Z_OBJ_P(ZEND_THIS)); + + OBJ_CHECK(obj); + ZEND_PARSE_PARAMETERS_NONE(); + + ION_TYPE t; + ION_CHECK(ion_reader_get_type(obj->reader, &t)); + switch (ION_TYPE_INT(t)) { + case tid_LIST_INT: + case tid_SEXP_INT: + case tid_STRUCT_INT: + RETVAL_TRUE; + break; + default: + RETVAL_FALSE; + break; + } +} +ZEND_METHOD(ion_Reader_Reader, getChildren) +{ + php_ion_reader *obj = php_ion_obj(reader, Z_OBJ_P(ZEND_THIS)); + OBJ_CHECK(obj); + + ZEND_PARSE_PARAMETERS_NONE(); + + ION_CHECK(ion_reader_step_in(obj->reader)); + + RETURN_ZVAL(ZEND_THIS, 1, 0); +} +ZEND_METHOD(ion_Reader_Reader, rewind) +{ + php_ion_reader *obj = php_ion_obj(reader, Z_OBJ_P(ZEND_THIS)); + OBJ_CHECK(obj); + + ZEND_PARSE_PARAMETERS_NONE(); + + ION_CHECK(ion_reader_next(obj->reader, &obj->state)); +} +ZEND_METHOD(ion_Reader_Reader, next) +{ + php_ion_reader *obj = php_ion_obj(reader, Z_OBJ_P(ZEND_THIS)); + OBJ_CHECK(obj); + + ZEND_PARSE_PARAMETERS_NONE(); + + if (obj->state == tid_EOF) { + SIZE depth = 0; + ION_CHECK(ion_reader_get_depth(obj->reader, &depth)); + if (depth) { + ION_CHECK(ion_reader_step_out(obj->reader)); + } + } + ION_CHECK(ion_reader_next(obj->reader, &obj->state)); +} +ZEND_METHOD(ion_Reader_Reader, valid) +{ + php_ion_reader *obj = php_ion_obj(reader, Z_OBJ_P(ZEND_THIS)); + OBJ_CHECK(obj); + + ZEND_PARSE_PARAMETERS_NONE(); + RETURN_BOOL(obj->state != tid_none && obj->state != tid_EOF); +} +ZEND_METHOD(ion_Reader_Reader, key) +{ + php_ion_reader *obj = php_ion_obj(reader, Z_OBJ_P(ZEND_THIS)); + OBJ_CHECK(obj); + + ZEND_PARSE_PARAMETERS_NONE(); + RETURN_IONTYPE(obj->state); +} +ZEND_METHOD(ion_Reader_Reader, current) +{ + ZEND_PARSE_PARAMETERS_NONE(); + RETURN_ZVAL(ZEND_THIS, 1, 0); +} +ZEND_METHOD(ion_Reader_Reader, getType) +{ + php_ion_reader *obj = php_ion_obj(reader, Z_OBJ_P(ZEND_THIS)); + OBJ_CHECK(obj); + + ZEND_PARSE_PARAMETERS_NONE(); + + ION_TYPE typ; + ION_CHECK(ion_reader_get_type(obj->reader, &typ)); + RETURN_IONTYPE(typ); +} +ZEND_METHOD(ion_Reader_Reader, hasAnnotations) +{ + php_ion_reader *obj = php_ion_obj(reader, Z_OBJ_P(ZEND_THIS)); + OBJ_CHECK(obj); + + ZEND_PARSE_PARAMETERS_NONE(); + + BOOL has = FALSE; + ION_CHECK(ion_reader_has_any_annotations(obj->reader, &has)); + RETURN_BOOL(has); +} +ZEND_METHOD(ion_Reader_Reader, hasAnnotation) +{ + php_ion_reader *obj = php_ion_obj(reader, Z_OBJ_P(ZEND_THIS)); + OBJ_CHECK(obj); + + zend_string *ann_zstr; + ZEND_PARSE_PARAMETERS_START(1, 1); + Z_PARAM_STR(ann_zstr); + ZEND_PARSE_PARAMETERS_END(); + + ION_STRING ann_istr; + BOOL has = FALSE; + ION_CHECK(ion_reader_has_annotation(obj->reader, ion_string_from_zend(&ann_istr, ann_zstr), &has)); + RETURN_BOOL(has); +} +ZEND_METHOD(ion_Reader_Reader, isNull) +{ + php_ion_reader *obj = php_ion_obj(reader, Z_OBJ_P(ZEND_THIS)); + OBJ_CHECK(obj); + + ZEND_PARSE_PARAMETERS_NONE(); + + BOOL is = FALSE; + ION_CHECK(ion_reader_is_null(obj->reader, &is)); + RETURN_BOOL(is); +} +ZEND_METHOD(ion_Reader_Reader, isInStruct) +{ + php_ion_reader *obj = php_ion_obj(reader, Z_OBJ_P(ZEND_THIS)); + OBJ_CHECK(obj); + + ZEND_PARSE_PARAMETERS_NONE(); + + BOOL is = FALSE; + ION_CHECK(ion_reader_is_in_struct(obj->reader, &is)); + RETURN_BOOL(is); +} +ZEND_METHOD(ion_Reader_Reader, getFieldName) +{ + php_ion_reader *obj = php_ion_obj(reader, Z_OBJ_P(ZEND_THIS)); + OBJ_CHECK(obj); + + ZEND_PARSE_PARAMETERS_NONE(); + + ION_STRING name; + ION_CHECK(ion_reader_get_field_name(obj->reader, &name)); + RETURN_STRINGL((char *) name.value, name.length); +} +ZEND_METHOD(ion_Reader_Reader, getFieldNameSymbol) +{ + php_ion_reader *obj = php_ion_obj(reader, Z_OBJ_P(ZEND_THIS)); + OBJ_CHECK(obj); + + ZEND_PARSE_PARAMETERS_NONE(); + + ION_SYMBOL *sym_ptr; + ION_CHECK(ion_reader_get_field_name_symbol(obj->reader, &sym_ptr)); + + php_ion_symbol_zval(sym_ptr, return_value); +} +ZEND_METHOD(ion_Reader_Reader, getAnnotations) +{ + php_ion_reader *obj = php_ion_obj(reader, Z_OBJ_P(ZEND_THIS)); + OBJ_CHECK(obj); + + ZEND_PARSE_PARAMETERS_NONE(); + + int32_t count, max; + if (obj->opt) { + max = php_ion_obj(reader_options, obj->opt)->opt.max_annotation_count; + } else { + max = 10; + } + ION_STRING *ptr = ecalloc(sizeof(*ptr), max); + iERR err = ion_reader_get_annotations(obj->reader, ptr, max, &count); + if (!err) { + array_init_size(return_value, count); + for (int32_t i = 0; i < count; ++i) { + add_next_index_str(return_value, zend_string_from_ion(&ptr[i])); + } + } + efree(ptr); + ION_CHECK(err); +} +ZEND_METHOD(ion_Reader_Reader, getAnnotationSymbols) +{ + php_ion_reader *obj = php_ion_obj(reader, Z_OBJ_P(ZEND_THIS)); + OBJ_CHECK(obj); + + ZEND_PARSE_PARAMETERS_NONE(); + + int32_t count, max = php_ion_obj(reader_options, obj->opt)->opt.max_annotation_count; + ION_SYMBOL *ptr = ecalloc(sizeof(*ptr), max); + iERR err = ion_reader_get_annotation_symbols(obj->reader, ptr, max, &count); + if (!err) { + array_init_size(return_value, count); + for (int32_t i = 0; i < count; ++i) { + zval zsym; + php_ion_symbol_zval(&ptr[i], &zsym); + add_next_index_zval(return_value, &zsym); + } + } + efree(ptr); + ION_CHECK(err); +} +ZEND_METHOD(ion_Reader_Reader, countAnnotations) +{ + php_ion_reader *obj = php_ion_obj(reader, Z_OBJ_P(ZEND_THIS)); + OBJ_CHECK(obj); + + ZEND_PARSE_PARAMETERS_NONE(); + + SIZE sz = 0; + ION_CHECK(ion_reader_get_annotation_count(obj->reader, &sz)); + RETURN_LONG(sz); +} +ZEND_METHOD(ion_Reader_Reader, getAnnotation) +{ + php_ion_reader *obj = php_ion_obj(reader, Z_OBJ_P(ZEND_THIS)); + OBJ_CHECK(obj); + + zend_long idx; + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_LONG(idx); + ZEND_PARSE_PARAMETERS_END(); + + ION_STRING ann; + ION_CHECK(ion_reader_get_an_annotation(obj->reader, idx, &ann)); + RETURN_STRINGL((char *) ann.value, ann.length); +} +ZEND_METHOD(ion_Reader_Reader, getAnnotationSymbol) +{ + php_ion_reader *obj = php_ion_obj(reader, Z_OBJ_P(ZEND_THIS)); + OBJ_CHECK(obj); + + zend_long idx; + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_LONG(idx); + ZEND_PARSE_PARAMETERS_END(); + + ION_SYMBOL sym; + ION_CHECK(ion_reader_get_an_annotation_symbol(obj->reader, idx, &sym)); + php_ion_symbol_zval(&sym, return_value); +} +ZEND_METHOD(ion_Reader_Reader, readNull) +{ + php_ion_reader *obj = php_ion_obj(reader, Z_OBJ_P(ZEND_THIS)); + OBJ_CHECK(obj); + + ZEND_PARSE_PARAMETERS_NONE(); + + ION_TYPE typ; + ION_CHECK(ion_reader_read_null(obj->reader, &typ)); + RETURN_OBJ_COPY(php_ion_type_fetch(typ)); +} +ZEND_METHOD(ion_Reader_Reader, readBool) +{ + php_ion_reader *obj = php_ion_obj(reader, Z_OBJ_P(ZEND_THIS)); + OBJ_CHECK(obj); + + ZEND_PARSE_PARAMETERS_NONE(); + + BOOL b; + ION_CHECK(ion_reader_read_bool(obj->reader, &b)); + RETURN_BOOL(b); +} +ZEND_METHOD(ion_Reader_Reader, readInt) +{ + php_ion_reader *obj = php_ion_obj(reader, Z_OBJ_P(ZEND_THIS)); + OBJ_CHECK(obj); + + ZEND_PARSE_PARAMETERS_NONE(); + + php_ion_unserialize_int(obj, return_value); +} +ZEND_METHOD(ion_Reader_Reader, readFloat) +{ + php_ion_reader *obj = php_ion_obj(reader, Z_OBJ_P(ZEND_THIS)); + OBJ_CHECK(obj); + + ZEND_PARSE_PARAMETERS_NONE(); + + ION_CHECK(ion_reader_read_double(obj->reader, &Z_DVAL_P(return_value))); +} +ZEND_METHOD(ion_Reader_Reader, readDecimal) +{ + php_ion_reader *obj = php_ion_obj(reader, Z_OBJ_P(ZEND_THIS)); + OBJ_CHECK(obj); + + ZEND_PARSE_PARAMETERS_NONE(); + + object_init_ex(return_value, ce_Decimal); + php_ion_decimal *dec = php_ion_obj(decimal, Z_OBJ_P(return_value)); + ION_CHECK(ion_reader_read_ion_decimal(obj->reader, &dec->dec)); + php_ion_decimal_ctor(dec); +} +ZEND_METHOD(ion_Reader_Reader, readTimestamp) +{ + php_ion_reader *obj = php_ion_obj(reader, Z_OBJ_P(ZEND_THIS)); + OBJ_CHECK(obj); + + ZEND_PARSE_PARAMETERS_NONE(); + + php_ion_unserialize_timestamp(obj, return_value); +} +ZEND_METHOD(ion_Reader_Reader, readSymbol) +{ + php_ion_reader *obj = php_ion_obj(reader, Z_OBJ_P(ZEND_THIS)); + OBJ_CHECK(obj); + + ZEND_PARSE_PARAMETERS_NONE(); + + ION_SYMBOL sym; + ION_CHECK(ion_reader_read_ion_symbol(obj->reader, &sym)); + php_ion_symbol_zval(&sym, return_value); +} +ZEND_METHOD(ion_Reader_Reader, readString) +{ + php_ion_reader *obj = php_ion_obj(reader, Z_OBJ_P(ZEND_THIS)); + OBJ_CHECK(obj); + + ZEND_PARSE_PARAMETERS_NONE(); + + ION_STRING str; + ION_CHECK(ion_reader_read_string(obj->reader, &str)); + RETURN_STRINGL((char *) str.value, str.length); +} + +typedef iERR (*read_part_fn)(ION_READER *, BYTE *, SIZE, SIZE *); +static void read_part(INTERNAL_FUNCTION_PARAMETERS, read_part_fn fn) +{ + php_ion_reader *obj = php_ion_obj(reader, Z_OBJ_P(ZEND_THIS)); + OBJ_CHECK(obj); + + zval *ref; + zend_string *zstr; + zend_long len = 0x1000; + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ZVAL(ref) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(len) + ZEND_PARSE_PARAMETERS_END(); + + ZVAL_DEREF(ref); + + if (Z_TYPE_P(ref) == IS_STRING && Z_STRLEN_P(ref) == len) { + /* reuse */ + zstr = Z_STR_P(ref); + } else { + zval_dtor(ref); + zstr = zend_string_alloc(len, 0); + } + + SIZE read = 0; + ION_CHECK(fn(obj->reader, (BYTE *) zstr->val, zstr->len, &read), goto fail); + if (read > 0) { + if (read < zstr->len) { + zstr = zend_string_truncate(zstr, read, 0); + } + ZVAL_STR(ref, zstr); + RETURN_TRUE; + } +fail: + if (zstr != Z_STR_P(ref)) { + zend_string_release(zstr); + } + ZVAL_EMPTY_STRING(ref); + RETURN_FALSE; +} +ZEND_METHOD(ion_Reader_Reader, readStringPart) +{ + read_part(INTERNAL_FUNCTION_PARAM_PASSTHRU, ion_reader_read_partial_string); +} +ZEND_METHOD(ion_Reader_Reader, readLob) +{ + php_ion_reader *obj = php_ion_obj(reader, Z_OBJ_P(ZEND_THIS)); + OBJ_CHECK(obj); + + ZEND_PARSE_PARAMETERS_NONE(); + + php_ion_unserialize_lob(obj, return_value); +} +ZEND_METHOD(ion_Reader_Reader, readLobPart) +{ + read_part(INTERNAL_FUNCTION_PARAM_PASSTHRU, ion_reader_read_lob_partial_bytes); +} +ZEND_METHOD(ion_Reader_Reader, getPosition) +{ + php_ion_reader *obj = php_ion_obj(reader, Z_OBJ_P(ZEND_THIS)); + OBJ_CHECK(obj); + + ZEND_PARSE_PARAMETERS_NONE(); + + int64_t bytes = 0; + int32_t dummy; + ION_CHECK(ion_reader_get_position(obj->reader, &bytes, &dummy, &dummy)); + RETURN_LONG(bytes); +} +ZEND_METHOD(ion_Reader_Reader, getDepth) +{ + php_ion_reader *obj = php_ion_obj(reader, Z_OBJ_P(ZEND_THIS)); + OBJ_CHECK(obj); + + ZEND_PARSE_PARAMETERS_NONE(); + + SIZE depth = 0; + ION_CHECK(ion_reader_get_depth(obj->reader, &depth)); + RETURN_LONG(depth); +} +ZEND_METHOD(ion_Reader_Reader, seek) +{ + php_ion_reader *obj = php_ion_obj(reader, Z_OBJ_P(ZEND_THIS)); + OBJ_CHECK(obj); + + zend_long off, len = -1; + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_LONG(off) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(len) + ZEND_PARSE_PARAMETERS_END(); + + ION_CHECK(ion_reader_seek(obj->reader, off, len)); +} +ZEND_METHOD(ion_Reader_Reader, getValueOffset) +{ + php_ion_reader *obj = php_ion_obj(reader, Z_OBJ_P(ZEND_THIS)); + OBJ_CHECK(obj); + + ZEND_PARSE_PARAMETERS_NONE(); + + POSITION off = 0; + ION_CHECK(ion_reader_get_value_offset(obj->reader, &off)); + RETURN_LONG(off); +} +ZEND_METHOD(ion_Reader_Reader, getValueLength) +{ + php_ion_reader *obj = php_ion_obj(reader, Z_OBJ_P(ZEND_THIS)); + OBJ_CHECK(obj); + + ZEND_PARSE_PARAMETERS_NONE(); + + SIZE len = 0; + ION_CHECK(ion_reader_get_value_length(obj->reader, &len)); + RETURN_LONG(len); +} +ZEND_METHOD(ion_Reader_Buffer_Reader, __construct) +{ + php_ion_reader *obj = php_ion_obj(reader, Z_OBJ_P(ZEND_THIS)); + PTR_CHECK(obj); + + zend_string *zstr; + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_STR(zstr) + Z_PARAM_OPTIONAL + Z_PARAM_OBJ_OF_CLASS_OR_NULL(obj->opt, ce_Reader_Options) + ZEND_PARSE_PARAMETERS_END(); + + obj->type = BUFFER_READER; + obj->buffer = zend_string_copy(zstr); + + php_ion_reader_ctor(obj); +} +ZEND_METHOD(ion_Reader_Buffer_Reader, getBuffer) +{ + php_ion_reader *obj = php_ion_obj(reader, Z_OBJ_P(ZEND_THIS)); + OBJ_CHECK(obj); + + ZEND_PARSE_PARAMETERS_NONE(); + RETURN_STR_COPY(obj->buffer); +} + +ZEND_METHOD(ion_Reader_Stream_Reader, __construct) +{ + php_ion_reader *obj = php_ion_obj(reader, Z_OBJ_P(ZEND_THIS)); + PTR_CHECK(obj); + + zval *zstream; + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_RESOURCE(zstream) + Z_PARAM_OPTIONAL + Z_PARAM_OBJ_OF_CLASS_OR_NULL(obj->opt, ce_Reader_Options) + ZEND_PARSE_PARAMETERS_END(); + + obj->type = STREAM_READER; + php_stream_from_zval_no_verify(obj->stream.ptr, zstream); + + php_ion_reader_ctor(obj); +} +ZEND_METHOD(ion_Reader_Stream_Reader, getStream) +{ + php_ion_reader *obj = php_ion_obj(reader, Z_OBJ_P(ZEND_THIS)); + OBJ_CHECK(obj); + + ZEND_PARSE_PARAMETERS_NONE(); + PTR_CHECK(obj->stream.ptr); + + GC_ADDREF(obj->stream.ptr->res); + RETURN_RES(obj->stream.ptr->res); +} +ZEND_METHOD(ion_Reader_Stream_Reader, resetStream) +{ + php_ion_reader *obj = php_ion_obj(reader, Z_OBJ_P(ZEND_THIS)); + OBJ_CHECK(obj); + + zval *zstream; + ZEND_PARSE_PARAMETERS_START(1, 1); + Z_PARAM_RESOURCE(zstream); + ZEND_PARSE_PARAMETERS_END(); + + ION_CHECK(ion_reader_reset_stream(&obj->reader, obj, php_ion_reader_stream_handler)); + + if (obj->stream.ptr) { + zend_list_delete(obj->stream.ptr->res); + } + php_stream_from_zval_no_verify(obj->stream.ptr, zstream); + PTR_CHECK(obj->stream.ptr); + Z_ADDREF_P(zstream); +} +ZEND_METHOD(ion_Reader_Stream_Reader, resetStreamWithLength) +{ + php_ion_reader *obj = php_ion_obj(reader, Z_OBJ_P(ZEND_THIS)); + OBJ_CHECK(obj); + + zval *zstream; + zend_long length; + ZEND_PARSE_PARAMETERS_START(1, 2); + Z_PARAM_RESOURCE(zstream); + Z_PARAM_LONG(length) + ZEND_PARSE_PARAMETERS_END(); + + ION_CHECK(ion_reader_reset_stream_with_length(&obj->reader, obj, php_ion_reader_stream_handler, length)); + + if (obj->stream.ptr) { + zend_list_delete(obj->stream.ptr->res); + } + php_stream_from_zval_no_verify(obj->stream.ptr, zstream); + PTR_CHECK(obj->stream.ptr); + Z_ADDREF_P(zstream); +} +ZEND_METHOD(ion_Writer_Options, __construct) +{ + php_ion_writer_options *obj = php_ion_obj(writer_options, Z_OBJ_P(ZEND_THIS)); + PTR_CHECK(obj); + + zend_bool binary = false, compact_floats = false, escape = false, pretty = false, + tabs = true, small_cntr_inl = true, suppress_sys = false, flush = false; + zend_long indent = 2, max_depth = 10, max_ann = 10, temp = 0x400, alloc = 0x1000; + ZEND_PARSE_PARAMETERS_START(0, 16) + Z_PARAM_OPTIONAL + //public readonly ?\ion\Catalog $catalog = null, + Z_PARAM_OBJ_OF_CLASS_OR_NULL(obj->cat, ce_Catalog) + //public readonly ?\ion\Collection $encodingSymbolTable = null, + Z_PARAM_OBJ_OF_CLASS_OR_NULL(obj->col, ce_Collection) + //public readonly ?\ion\Decimal\Context $decimalContext = null, + Z_PARAM_OBJ_OF_CLASS_OR_NULL(obj->dec_ctx, ce_Decimal_Context) + //public readonly bool $outputBinary = false, + Z_PARAM_BOOL(binary) + //public readonly bool $compactFloats = false, + Z_PARAM_BOOL(compact_floats) + //public readonly bool $escapeNonAscii = false, + Z_PARAM_BOOL(escape) + //public readonly bool $prettyPrint = false, + Z_PARAM_BOOL(pretty) + //public readonly bool $indentTabs = true, + Z_PARAM_BOOL(tabs) + //public readonly int $indentSize = 2, + Z_PARAM_LONG(indent) + //public readonly bool $smallContainersInline = true, + Z_PARAM_BOOL(small_cntr_inl) + //public readonly bool $suppressSystemValues = false, + Z_PARAM_BOOL(suppress_sys) + //public readonly bool $flushEveryValue = false, + Z_PARAM_BOOL(flush) + //public readonly int $maxContainerDepth = 10, + Z_PARAM_LONG(max_depth) + //public readonly int $maxAnnotations = 10, + Z_PARAM_LONG(max_ann) + //public readonly int $tempBufferSize = 0x400, + Z_PARAM_LONG(temp) + //public readonly int $allocationPageSize = 0x1000, + Z_PARAM_LONG(alloc) + ZEND_PARSE_PARAMETERS_END(); + + if (obj->cat) { + update_property_obj(&obj->std, ZEND_STRL("catalog"), obj->cat); + obj->opt.pcatalog = php_ion_obj(catalog, obj->cat)->cat; + } + if (obj->col) { + // TODO + } + if (obj->dec_ctx) { + update_property_obj(&obj->std, ZEND_STRL("decimalContext"), obj->dec_ctx); + obj->opt.decimal_context = &php_ion_obj(decimal_ctx, obj->dec_ctx)->ctx; + } + zend_update_property_bool(obj->std.ce, &obj->std, ZEND_STRL("outputBinary"), + obj->opt.output_as_binary = binary); + zend_update_property_bool(obj->std.ce, &obj->std, ZEND_STRL("comactFloats"), + obj->opt.compact_floats = compact_floats); + zend_update_property_bool(obj->std.ce, &obj->std, ZEND_STRL("excapeNonAscii"), + obj->opt.escape_all_non_ascii = escape); + zend_update_property_bool(obj->std.ce, &obj->std, ZEND_STRL("prettyPrint"), + obj->opt.pretty_print = pretty); + zend_update_property_bool(obj->std.ce, &obj->std, ZEND_STRL("indentTabs"), + obj->opt.indent_with_tabs = tabs); + zend_update_property_long(obj->std.ce, &obj->std, ZEND_STRL("indentSize"), + obj->opt.indent_size = indent); + zend_update_property_bool(obj->std.ce, &obj->std, ZEND_STRL("smallContainersInline"), + obj->opt.small_containers_in_line = small_cntr_inl); + zend_update_property_bool(obj->std.ce, &obj->std, ZEND_STRL("suppressSystemValues"), + obj->opt.supress_system_values = suppress_sys); + zend_update_property_bool(obj->std.ce, &obj->std, ZEND_STRL("flushEveryValue"), + obj->opt.flush_every_value = flush); + zend_update_property_long(obj->std.ce, &obj->std, ZEND_STRL("maxContainerDepth"), + obj->opt.max_container_depth = max_depth); + zend_update_property_long(obj->std.ce, &obj->std, ZEND_STRL("maxAnnotations"), + obj->opt.max_annotation_count = max_ann); + zend_update_property_long(obj->std.ce, &obj->std, ZEND_STRL("tempBufferSize"), + obj->opt.temp_buffer_size = temp); + zend_update_property_long(obj->std.ce, &obj->std, ZEND_STRL("allocationPageSize"), + obj->opt.allocation_page_size = alloc); +} +ZEND_METHOD(ion_Writer_Writer, writeNull) +{ + php_ion_writer *obj = php_ion_obj(writer, Z_OBJ_P(ZEND_THIS)); + OBJ_CHECK(obj); + + ZEND_PARSE_PARAMETERS_NONE(); + + ION_CHECK(ion_writer_write_null(obj->writer)); +} +ZEND_METHOD(ion_Writer_Writer, writeTypedNull) +{ + php_ion_writer *obj = php_ion_obj(writer, Z_OBJ_P(ZEND_THIS)); + OBJ_CHECK(obj); + + zend_object *typ_obj; + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_OBJ_OF_CLASS(typ_obj, ce_Type) + ZEND_PARSE_PARAMETERS_END(); + + php_ion_type *typ = php_ion_obj(type, typ_obj); + OBJ_CHECK(typ); + ION_CHECK(ion_writer_write_typed_null(obj->writer, php_ion_obj(type, typ)->typ)); +} +ZEND_METHOD(ion_Writer_Writer, writeBool) +{ + php_ion_writer *obj = php_ion_obj(writer, Z_OBJ_P(ZEND_THIS)); + OBJ_CHECK(obj); + + zend_bool b; + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_BOOL(b) + ZEND_PARSE_PARAMETERS_END(); + + ION_CHECK(ion_writer_write_bool(obj->writer, b)); +} +ZEND_METHOD(ion_Writer_Writer, writeInt) +{ + php_ion_writer *obj = php_ion_obj(writer, Z_OBJ_P(ZEND_THIS)); + OBJ_CHECK(obj); + + zend_long l; + zend_string *s; + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_STR_OR_LONG(s, l) + ZEND_PARSE_PARAMETERS_END(); + + if (s) { + ION_INT *i; + ION_CHECK(ion_int_alloc(obj->writer, &i)); + ION_CHECK(ion_int_from_chars(i, s->val, s->len)); + ION_CHECK(ion_writer_write_ion_int(obj->writer, i)); + ion_int_free(i); + } else { + ION_CHECK(ion_writer_write_int64(obj->writer, l)); + } +} +ZEND_METHOD(ion_Writer_Writer, writeFloat) +{ + php_ion_writer *obj = php_ion_obj(writer, Z_OBJ_P(ZEND_THIS)); + OBJ_CHECK(obj); + + double d; + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_DOUBLE(d) + ZEND_PARSE_PARAMETERS_END(); + + ION_CHECK(ion_writer_write_double(obj->writer, d)); +} +ZEND_METHOD(ion_Writer_Writer, writeDecimal) +{ + php_ion_writer *obj = php_ion_obj(writer, Z_OBJ_P(ZEND_THIS)); + OBJ_CHECK(obj); + + zend_object *dec_obj; + zend_string *dec_str; + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_OBJ_OF_CLASS_OR_STR(dec_obj, ce_Decimal, dec_str) + ZEND_PARSE_PARAMETERS_END(); + + if (dec_str) { + ION_STRING s; + ION_CHECK(ion_writer_write_string(obj->writer, ion_string_from_zend(&s, dec_str))); + } else { + php_ion_decimal *dec = php_ion_obj(decimal, dec_obj); + ION_CHECK(ion_writer_write_ion_decimal(obj->writer, &dec->dec)); + } +} +ZEND_METHOD(ion_Writer_Writer, writeTimestamp) +{ + php_ion_writer *obj = php_ion_obj(writer, Z_OBJ_P(ZEND_THIS)); + OBJ_CHECK(obj); + + zend_object *ts_obj; + zend_string *ts_str; + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_OBJ_OF_CLASS_OR_STR(ts_obj, ce_Timestamp, ts_str) + ZEND_PARSE_PARAMETERS_END(); + + decContext *ctx = NULL; + if (obj->opt) { + ctx = php_ion_obj(reader_options, obj->opt)->opt.decimal_context; + } + + ION_TIMESTAMP tmp = {0}; + if (ts_str) { + SIZE used; + ION_CHECK(ion_timestamp_parse(&tmp, ts_str->val, ts_str->len, &used, ctx)); + } else { + php_ion_timestamp *ts = php_ion_obj(timestamp, ts_obj); + OBJ_CHECK(ts); + ion_timestamp_from_php(&tmp, ts, ctx); + } + ION_CHECK(ion_writer_write_timestamp(obj->writer, &tmp)); +} +ZEND_METHOD(ion_Writer_Writer, writeSymbol) +{ + php_ion_writer *obj = php_ion_obj(writer, Z_OBJ_P(ZEND_THIS)); + OBJ_CHECK(obj); + + zend_object *sym_obj; + zend_string *sym_str; + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_OBJ_OF_CLASS_OR_STR(sym_obj, ce_Symbol, sym_str); + ZEND_PARSE_PARAMETERS_END(); + + if (sym_str) { + ION_STRING is; + ION_CHECK(ion_writer_write_symbol(obj->writer, ion_string_from_zend(&is, sym_str))); + } else { + php_ion_symbol *sym = php_ion_obj(symbol, sym_obj); + PTR_CHECK(sym); + ION_CHECK(ion_writer_write_ion_symbol(obj->writer, &sym->sym)); + } +} +ZEND_METHOD(ion_Writer_Writer, writeString) +{ + php_ion_writer *obj = php_ion_obj(writer, Z_OBJ_P(ZEND_THIS)); + OBJ_CHECK(obj); + + zend_string *str; + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_STR(str) + ZEND_PARSE_PARAMETERS_END(); + + ION_STRING is; + ION_CHECK(ion_writer_write_string(obj->writer, ion_string_from_zend(&is, str))); +} +ZEND_METHOD(ion_Writer_Writer, writeCLob) +{ + php_ion_writer *obj = php_ion_obj(writer, Z_OBJ_P(ZEND_THIS)); + OBJ_CHECK(obj); + + zend_string *str; + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_STR(str) + ZEND_PARSE_PARAMETERS_END(); + + ION_CHECK(ion_writer_write_clob(obj->writer, (BYTE *) str->val, str->len)); +} +ZEND_METHOD(ion_Writer_Writer, writeBLob) +{ + php_ion_writer *obj = php_ion_obj(writer, Z_OBJ_P(ZEND_THIS)); + OBJ_CHECK(obj); + + zend_string *str; + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_STR(str) + ZEND_PARSE_PARAMETERS_END(); + + ION_CHECK(ion_writer_write_blob(obj->writer, (BYTE *) str->val, str->len)); +} +ZEND_METHOD(ion_Writer_Writer, startLob) +{ + php_ion_writer *obj = php_ion_obj(writer, Z_OBJ_P(ZEND_THIS)); + OBJ_CHECK(obj); + + zend_object *typ_obj; + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_OBJ_OF_CLASS(typ_obj, ce_Type) + ZEND_PARSE_PARAMETERS_END(); + + php_ion_type *typ = php_ion_obj(type, typ_obj); + OBJ_CHECK(typ); + ION_CHECK(ion_writer_start_lob(obj->writer, php_ion_obj(type, typ)->typ)); +} +ZEND_METHOD(ion_Writer_Writer, appendLob) +{ + php_ion_writer *obj = php_ion_obj(writer, Z_OBJ_P(ZEND_THIS)); + OBJ_CHECK(obj); + + zend_string *str; + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_STR(str) + ZEND_PARSE_PARAMETERS_END(); + + ION_CHECK(ion_writer_append_lob(obj->writer, (BYTE *) str->val, str->len)); +} +ZEND_METHOD(ion_Writer_Writer, finishLob) +{ + php_ion_writer *obj = php_ion_obj(writer, Z_OBJ_P(ZEND_THIS)); + OBJ_CHECK(obj); + + ZEND_PARSE_PARAMETERS_NONE(); + + ION_CHECK(ion_writer_finish_lob(obj->writer)); +} +ZEND_METHOD(ion_Writer_Writer, startContainer) +{ + php_ion_writer *obj = php_ion_obj(writer, Z_OBJ_P(ZEND_THIS)); + OBJ_CHECK(obj); + + zend_object *typ_obj; + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_OBJ_OF_CLASS(typ_obj, ce_Type) + ZEND_PARSE_PARAMETERS_END(); + + php_ion_type *typ = php_ion_obj(type, typ_obj); + OBJ_CHECK(typ); + ION_CHECK(ion_writer_start_container(obj->writer, php_ion_obj(type, typ)->typ)); +} +ZEND_METHOD(ion_Writer_Writer, finishContainer) +{ + php_ion_writer *obj = php_ion_obj(writer, Z_OBJ_P(ZEND_THIS)); + OBJ_CHECK(obj); + + ZEND_PARSE_PARAMETERS_NONE(); + + ION_CHECK(ion_writer_finish_container(obj->writer)); +} +ZEND_METHOD(ion_Writer_Writer, writeFieldName) +{ + php_ion_writer *obj = php_ion_obj(writer, Z_OBJ_P(ZEND_THIS)); + OBJ_CHECK(obj); + + zend_object *sym_obj; + zend_string *sym_str; + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_OBJ_OF_CLASS_OR_STR(sym_obj, ce_Symbol, sym_str); + ZEND_PARSE_PARAMETERS_END(); + + if (sym_str) { + ION_STRING is; + ION_CHECK(ion_writer_write_field_name(obj->writer, ion_string_from_zend(&is, sym_str))); + } else { + php_ion_symbol *sym = php_ion_obj(symbol, sym_obj); + PTR_CHECK(sym); + ION_CHECK(ion_writer_write_field_name_symbol(obj->writer, &sym->sym)); + } +} +ZEND_METHOD(ion_Writer_Writer, writeAnnotation) +{ + php_ion_writer *obj = php_ion_obj(writer, Z_OBJ_P(ZEND_THIS)); + OBJ_CHECK(obj); + + zval *args; + unsigned argc; + ZEND_PARSE_PARAMETERS_START(1, -1) + Z_PARAM_VARIADIC('+', args, argc); + ZEND_PARSE_PARAMETERS_END(); + + for (unsigned i = 0; i < argc; ++i) { + switch (Z_TYPE(args[i])) { + case IS_STRING: + ION_STRING is; + ION_CHECK(ion_writer_add_annotation(obj->writer, ion_string_from_zend(&is, Z_STR(args[i])))); + break; + + case IS_OBJECT: + ION_CHECK(ion_writer_add_annotation_symbol(obj->writer, &php_ion_obj(symbol, Z_OBJ(args[i]))->sym)); + break; + } + } +} +ZEND_METHOD(ion_Writer_Writer, getDepth) +{ + php_ion_writer *obj = php_ion_obj(writer, Z_OBJ_P(ZEND_THIS)); + OBJ_CHECK(obj); + + ZEND_PARSE_PARAMETERS_NONE(); + + SIZE depth; + ION_CHECK(ion_writer_get_depth(obj->writer, &depth)); + RETURN_LONG(depth); +} +ZEND_METHOD(ion_Writer_Writer, flush) +{ + php_ion_writer *obj = php_ion_obj(writer, Z_OBJ_P(ZEND_THIS)); + OBJ_CHECK(obj); + + ZEND_PARSE_PARAMETERS_NONE(); + + SIZE flushed; + ION_CHECK(ion_writer_flush(obj->writer, &flushed)); + if (obj->type == BUFFER_WRITER) { + smart_str_0(&obj->buffer.str); + } + RETURN_LONG(flushed); +} +ZEND_METHOD(ion_Writer_Writer, finish) +{ + php_ion_writer *obj = php_ion_obj(writer, Z_OBJ_P(ZEND_THIS)); + OBJ_CHECK(obj); + + ZEND_PARSE_PARAMETERS_NONE(); + + SIZE flushed; + ION_CHECK(ion_writer_finish(obj->writer, &flushed)); + if (obj->type == BUFFER_WRITER) { + smart_str_0(&obj->buffer.str); + } + RETURN_LONG(flushed); +} +ZEND_METHOD(ion_Writer_Writer, writeOne) +{ +} +ZEND_METHOD(ion_Writer_Writer, writeAll) +{ +} +ZEND_METHOD(ion_Writer_Buffer_Writer, __construct) +{ + php_ion_writer *obj = php_ion_obj(writer, Z_OBJ_P(ZEND_THIS)); + PTR_CHECK(obj); + + zval *ref; + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_ZVAL(ref) + Z_PARAM_OPTIONAL + Z_PARAM_OBJ_OF_CLASS_OR_NULL(obj->opt, ce_Writer_Options) + ZEND_PARSE_PARAMETERS_END(); + + obj->type = BUFFER_WRITER; + ZVAL_COPY(&obj->buffer.val, ref); + zval_dtor(Z_REFVAL_P(ref)); + + php_ion_writer_ctor(obj); +} +ZEND_METHOD(ion_Writer_Buffer_Writer, getBuffer) +{ + php_ion_writer *obj = php_ion_obj(writer, Z_OBJ_P(ZEND_THIS)); + OBJ_CHECK(obj); + + ZEND_PARSE_PARAMETERS_NONE(); + + RETVAL_STR(zend_string_dup(obj->buffer.str.s, 0)); +} +ZEND_METHOD(ion_Writer_Stream_Writer, __construct) +{ + php_ion_writer *obj = php_ion_obj(writer, Z_OBJ_P(ZEND_THIS)); + PTR_CHECK(obj); + + zval *zstream; + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_RESOURCE(zstream) + Z_PARAM_OPTIONAL + Z_PARAM_OBJ_OF_CLASS_OR_NULL(obj->opt, ce_Writer_Options) + ZEND_PARSE_PARAMETERS_END(); + + obj->type = STREAM_WRITER; + php_stream_from_zval_no_verify(obj->stream.ptr, zstream); + + php_ion_writer_ctor(obj); +} +ZEND_METHOD(ion_Writer_Stream_Writer, getStream) +{ + php_ion_writer *obj = php_ion_obj(writer, Z_OBJ_P(ZEND_THIS)); + OBJ_CHECK(obj); + + ZEND_PARSE_PARAMETERS_NONE(); + PTR_CHECK(obj->stream.ptr); + + GC_ADDREF(obj->stream.ptr->res); + RETURN_RES(obj->stream.ptr->res); +} + +ZEND_METHOD(ion_PHP_Serializer, __construct) +{ + zend_object *obj = Z_OBJ_P(ZEND_THIS); + + zend_object *writer_obj; + zend_bool call_magic = true; + zend_string *call_custom = NULL; + ZEND_PARSE_PARAMETERS_START(1, 3) + Z_PARAM_OBJ_OF_CLASS(writer_obj, ce_Writer) + Z_PARAM_OPTIONAL + Z_PARAM_BOOL(call_magic) + Z_PARAM_STR_OR_NULL(call_custom) + ZEND_PARSE_PARAMETERS_END(); + + update_property_obj(obj, ZEND_STRL("writer"), writer_obj); + zend_update_property_bool(obj->ce, obj, ZEND_STRL("callMagicSerialize"), + php_ion_obj(writer, writer_obj)->php.call_magic_serialize = call_magic); + if (call_custom) { + zend_update_property_str(obj->ce, obj, ZEND_STRL("callCustomSerialize"), call_custom); + php_ion_obj(writer, writer_obj)->php.custom_serialize = zend_string_tolower(call_custom); + } else { + zend_update_property_null(obj->ce, obj, ZEND_STRL("callCustomSerialize")); + } +} +ZEND_METHOD(ion_PHP_Serializer, __invoke) +{ + zend_object *obj = Z_OBJ_P(ZEND_THIS); + + zval *data; + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ZVAL(data) + ZEND_PARSE_PARAMETERS_END(); + + zval tmp, *zwriter; + if ((zwriter = zend_read_property(obj->ce, obj, ZEND_STRL("writer"), 0, &tmp))) { + if (Z_OBJCE_P(zwriter) == ce_Writer_Buffer_Writer || Z_OBJCE_P(zwriter) == ce_Writer_Stream_Writer) { + php_ion_writer *writer = php_ion_obj(writer, Z_OBJ_P(zwriter)); + php_ion_serialize(writer, data, &writer->buffer.val); + RETURN_STR_COPY(writer->buffer.str.s); + } else { + zend_call_method_with_1_params(obj, obj->ce, NULL /* TODO */, "serialize", return_value, data); + } + } +} +ZEND_METHOD(ion_PHP_Serializer, serialize) +{ + //zend_object *obj = Z_OBJ_P(ZEND_THIS); + + zval *data; + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ZVAL(data) + ZEND_PARSE_PARAMETERS_END(); + + // TODO +} +ZEND_METHOD(ion_PHP_Unserializer, __construct) +{ + zend_object *obj = Z_OBJ_P(ZEND_THIS); + + zend_object *reader_obj; + zend_bool call_magic = true; + zend_string *call_custom = NULL; + ZEND_PARSE_PARAMETERS_START(1, 3) + Z_PARAM_OBJ_OF_CLASS(reader_obj, ce_Reader) + Z_PARAM_OPTIONAL + Z_PARAM_BOOL(call_magic) + Z_PARAM_STR_OR_NULL(call_custom) + ZEND_PARSE_PARAMETERS_END(); + + update_property_obj(obj, ZEND_STRL("reader"), reader_obj); + zend_update_property_bool(obj->ce, obj, ZEND_STRL("callMagicUnserialize"), + php_ion_obj(reader, reader_obj)->php.call_magic_unserialize = call_magic); + if (call_custom) { + zend_update_property_str(obj->ce, obj, ZEND_STRL("callCustomUnserialize"), call_custom); + php_ion_obj(reader, reader_obj)->php.custom_unserialize = zend_string_tolower(call_custom); + } else { + zend_update_property_null(obj->ce, obj, ZEND_STRL("callCustomUnserialize")); + } +} +ZEND_METHOD(ion_PHP_Unserializer, __invoke) +{ + zend_object *obj = Z_OBJ_P(ZEND_THIS); + + zval zreader; + if (zend_read_property(obj->ce, obj, ZEND_STRL("reader"), 0, &zreader)) { + if (Z_OBJCE(zreader) == ce_Reader_Buffer_Reader || Z_OBJCE(zreader) == ce_Reader_Stream_Reader) { + php_ion_unserialize(php_ion_obj(reader, Z_OBJ(zreader)), NULL, return_value); + } else { + zend_call_method_with_0_params(obj, obj->ce, NULL /* TODO */, "unserialize", return_value); + } + } +} +ZEND_METHOD(ion_PHP_Unserializer, unserialize) +{ + //zend_object *obj = Z_OBJ_P(ZEND_THIS); + + ZEND_PARSE_PARAMETERS_NONE(); + + // TODO +} + +ZEND_FUNCTION(ion_serialize) +{ + zval *zv; + zend_object *zo_opt = NULL; + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_ZVAL(zv) + Z_PARAM_OPTIONAL + Z_PARAM_OBJ_OF_CLASS_OR_NULL(zo_opt, ce_Writer_Options) + ZEND_PARSE_PARAMETERS_END(); + + zval zwriter; + object_init_ex(&zwriter, ce_Writer_Buffer_Writer); + + php_ion_writer *obj = php_ion_obj(writer, Z_OBJ(zwriter)); + obj->opt = zo_opt; + obj->type = BUFFER_WRITER; + + php_ion_writer_ctor(obj); + + php_ion_serialize(obj, zv, return_value); + zval_ptr_dtor(&zwriter); +} +ZEND_FUNCTION(ion_unserialize) +{ + zend_string *zstr; + zend_object *zo_opt = NULL; + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_STR(zstr) + Z_PARAM_OPTIONAL + Z_PARAM_OBJ_OF_CLASS_OR_NULL(zo_opt, ce_Reader_Options) + ZEND_PARSE_PARAMETERS_END(); + + zval zreader; + object_init_ex(&zreader, ce_Reader_Buffer_Reader); + + php_ion_reader *obj = php_ion_obj(reader, Z_OBJ(zreader)); + obj->opt = zo_opt; + obj->type = BUFFER_READER; + obj->buffer = zend_string_copy(zstr); + + php_ion_reader_ctor(obj); + php_ion_unserialize(obj, NULL, return_value); + zval_ptr_dtor(&zreader); +} + +PHP_RINIT_FUNCTION(ion) +{ +#if defined(ZTS) && defined(COMPILE_DL_ION) + ZEND_TSRMLS_CACHE_UPDATE(); +#endif + + php_ion_globals_serializer_init(); + php_ion_globals_unserializer_init(); + return SUCCESS; +} + +PHP_RSHUTDOWN_FUNCTION(ion) +{ + php_ion_globals_serializer_dtor(); + php_ion_globals_unserializer_dtor(); + return SUCCESS; +} + +php_ion_decl(type, Type); +php_ion_decl(symbol, Symbol, php_ion_symbol_dtor(obj)); +php_ion_decl(symbol_iloc, Symbol_ImportLocation, php_ion_symbol_iloc_dtor(obj)); +php_ion_decl(symbol_table, Symbol_Table); +php_ion_decl(decimal, Decimal, php_ion_decimal_dtor(obj)); +php_ion_decl(decimal_ctx, Decimal_Context); +php_ion_decl(timestamp, Timestamp, php_ion_timestamp_dtor(obj)); +php_ion_decl(catalog, Catalog); +php_ion_decl(reader_options, Reader_Options); +php_ion_decl(reader, Reader_Reader, php_ion_reader_dtor(obj)); +php_ion_decl(writer_options, Writer_Options); +php_ion_decl(writer, Writer_Writer, php_ion_writer_dtor(obj)); + +PHP_MINIT_FUNCTION(ion) +{ + ce_Annotation = register_class_ion_Annotation(); + + php_ion_register(type, Type); + php_ion_register(symbol, Symbol); + php_ion_register(symbol_iloc, Symbol_ImportLocation); + php_ion_register(symbol_table, Symbol_Table); + + ce_Symbol_System = register_class_ion_Symbol_System(); + ce_Symbol_System_SID = register_class_ion_Symbol_System_SID(); + + ce_Collection = register_class_ion_Collection(); + + php_ion_register(decimal, Decimal); + oh_decimal.get_properties_for = php_ion_decimal_get_props_for; + php_ion_register(decimal_ctx, Decimal_Context); + php_ion_register(timestamp, Timestamp, php_date_get_date_ce()); + php_ion_register(catalog, Catalog); + + ce_Reader = register_class_ion_Reader(spl_ce_RecursiveIterator); + + php_ion_register(reader_options, Reader_Options); + php_ion_register(reader, Reader_Reader, ce_Reader); + + ce_Reader_Buffer = register_class_ion_Reader_Buffer(ce_Reader); + ce_Reader_Buffer_Reader = register_class_ion_Reader_Buffer_Reader(ce_Reader_Reader, ce_Reader_Buffer); + ce_Reader_Stream = register_class_ion_Reader_Stream(ce_Reader); + ce_Reader_Stream_Reader = register_class_ion_Reader_Stream_Reader(ce_Reader_Reader, ce_Reader_Stream); + + ce_Writer = register_class_ion_Writer(); + + php_ion_register(writer_options, Writer_Options); + php_ion_register(writer, Writer_Writer, ce_Writer); + + ce_Writer_Buffer = register_class_ion_Writer_Buffer(ce_Writer); + ce_Writer_Buffer_Writer = register_class_ion_Writer_Buffer_Writer(ce_Writer_Writer, ce_Writer_Buffer); + ce_Writer_Stream = register_class_ion_Writer_Stream(ce_Writer); + ce_Writer_Stream_Writer = register_class_ion_Writer_Stream_Writer(ce_Writer_Writer, ce_Writer_Stream); + + ce_PHP_Serializer = register_class_ion_PHP_Serializer(); + ce_PHP_Unserializer = register_class_ion_PHP_Unserializer(); + + return SUCCESS; +} + +PHP_MINFO_FUNCTION(ion) +{ + php_info_print_table_start(); + php_info_print_table_header(2, "ion support", "enabled"); + php_info_print_table_end(); +} + +PHP_GINIT_FUNCTION(ion) +{ + memset(ion_globals, 0, sizeof(*ion_globals)); +} +PHP_GSHUTDOWN_FUNCTION(ion) +{ +} + +static zend_module_dep ion_module_deps[] = { + ZEND_MOD_REQUIRED("spl") + ZEND_MOD_END +}; + +zend_module_entry ion_module_entry = { + STANDARD_MODULE_HEADER_EX, + NULL, + ion_module_deps, + "ion", /* Extension name */ + ext_functions, /* zend_function_entry */ + PHP_MINIT(ion), /* PHP_MINIT - Module initialization */ + NULL, /* PHP_MSHUTDOWN - Module shutdown */ + PHP_RINIT(ion), /* PHP_RINIT - Request initialization */ + PHP_RSHUTDOWN(ion), /* PHP_RSHUTDOWN - Request shutdown */ + PHP_MINFO(ion), /* PHP_MINFO - Module info */ + PHP_ION_VERSION, /* Version */ + ZEND_MODULE_GLOBALS(ion), + PHP_GINIT(ion), /* PHP_GINIT */ + PHP_GSHUTDOWN(ion), /* PHP_GSHUTDOWN */ + NULL, + STANDARD_MODULE_PROPERTIES_EX +}; + +#ifdef COMPILE_DL_ION +# ifdef ZTS +ZEND_TSRMLS_CACHE_DEFINE() +# endif +ZEND_GET_MODULE(ion) +#endif diff --git a/ion.stub.php b/ion.stub.php new file mode 100644 index 0000000..8225a1b --- /dev/null +++ b/ion.stub.php @@ -0,0 +1,446 @@ +ce_flags |= ZEND_ACC_ABSTRACT; + zend_class_implements(class_entry, 1, class_entry_ion_Reader); + + zend_string *property_options_class_ion_Reader_Options = zend_string_init("ion\\Reader\\Options", sizeof("ion\\Reader\\Options")-1, 1); + zval property_options_default_value; + ZVAL_UNDEF(&property_options_default_value); + zend_string *property_options_name = zend_string_init("options", sizeof("options") - 1, 1); + zend_declare_typed_property(class_entry, property_options_name, &property_options_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_options_class_ion_Reader_Options, 0, MAY_BE_NULL)); + zend_string_release(property_options_name); + + return class_entry; +} + +static zend_class_entry *register_class_ion_Reader_Buffer(zend_class_entry *class_entry_ion_Reader) +{ + zend_class_entry ce, *class_entry; + + INIT_NS_CLASS_ENTRY(ce, "ion\\Reader", "Buffer", class_ion_Reader_Buffer_methods); + class_entry = zend_register_internal_interface(&ce); + zend_class_implements(class_entry, 1, class_entry_ion_Reader); + + return class_entry; +} + +static zend_class_entry *register_class_ion_Reader_Buffer_Reader(zend_class_entry *class_entry_ion_Reader_Reader, zend_class_entry *class_entry_ion_Reader_Buffer) +{ + zend_class_entry ce, *class_entry; + + INIT_NS_CLASS_ENTRY(ce, "ion\\Reader\\Buffer", "Reader", class_ion_Reader_Buffer_Reader_methods); + class_entry = zend_register_internal_class_ex(&ce, class_entry_ion_Reader_Reader); + zend_class_implements(class_entry, 1, class_entry_ion_Reader_Buffer); + + return class_entry; +} + +static zend_class_entry *register_class_ion_Reader_Stream(zend_class_entry *class_entry_ion_Reader) +{ + zend_class_entry ce, *class_entry; + + INIT_NS_CLASS_ENTRY(ce, "ion\\Reader", "Stream", class_ion_Reader_Stream_methods); + class_entry = zend_register_internal_interface(&ce); + zend_class_implements(class_entry, 1, class_entry_ion_Reader); + + return class_entry; +} + +static zend_class_entry *register_class_ion_Reader_Stream_Reader(zend_class_entry *class_entry_ion_Reader_Reader, zend_class_entry *class_entry_ion_Reader_Stream) +{ + zend_class_entry ce, *class_entry; + + INIT_NS_CLASS_ENTRY(ce, "ion\\Reader\\Stream", "Reader", class_ion_Reader_Stream_Reader_methods); + class_entry = zend_register_internal_class_ex(&ce, class_entry_ion_Reader_Reader); + zend_class_implements(class_entry, 1, class_entry_ion_Reader_Stream); + + return class_entry; +} + +static zend_class_entry *register_class_ion_Writer_Options(void) +{ + zend_class_entry ce, *class_entry; + + INIT_NS_CLASS_ENTRY(ce, "ion\\Writer", "Options", class_ion_Writer_Options_methods); + class_entry = zend_register_internal_class_ex(&ce, NULL); + + zend_string *property_catalog_class_ion_Catalog = zend_string_init("ion\\Catalog", sizeof("ion\\Catalog")-1, 1); + zval property_catalog_default_value; + ZVAL_UNDEF(&property_catalog_default_value); + zend_string *property_catalog_name = zend_string_init("catalog", sizeof("catalog") - 1, 1); + zend_declare_typed_property(class_entry, property_catalog_name, &property_catalog_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_catalog_class_ion_Catalog, 0, MAY_BE_NULL)); + zend_string_release(property_catalog_name); + + zend_string *property_decimalContext_class_ion_Decimal_Context = zend_string_init("ion\\Decimal\\Context", sizeof("ion\\Decimal\\Context")-1, 1); + zval property_decimalContext_default_value; + ZVAL_UNDEF(&property_decimalContext_default_value); + zend_string *property_decimalContext_name = zend_string_init("decimalContext", sizeof("decimalContext") - 1, 1); + zend_declare_typed_property(class_entry, property_decimalContext_name, &property_decimalContext_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_decimalContext_class_ion_Decimal_Context, 0, MAY_BE_NULL)); + zend_string_release(property_decimalContext_name); + + zval property_outputBinary_default_value; + ZVAL_UNDEF(&property_outputBinary_default_value); + zend_string *property_outputBinary_name = zend_string_init("outputBinary", sizeof("outputBinary") - 1, 1); + zend_declare_typed_property(class_entry, property_outputBinary_name, &property_outputBinary_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_BOOL)); + zend_string_release(property_outputBinary_name); + + zval property_compactFloats_default_value; + ZVAL_UNDEF(&property_compactFloats_default_value); + zend_string *property_compactFloats_name = zend_string_init("compactFloats", sizeof("compactFloats") - 1, 1); + zend_declare_typed_property(class_entry, property_compactFloats_name, &property_compactFloats_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_BOOL)); + zend_string_release(property_compactFloats_name); + + zval property_escapeNonAscii_default_value; + ZVAL_UNDEF(&property_escapeNonAscii_default_value); + zend_string *property_escapeNonAscii_name = zend_string_init("escapeNonAscii", sizeof("escapeNonAscii") - 1, 1); + zend_declare_typed_property(class_entry, property_escapeNonAscii_name, &property_escapeNonAscii_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_BOOL)); + zend_string_release(property_escapeNonAscii_name); + + zval property_prettyPrint_default_value; + ZVAL_UNDEF(&property_prettyPrint_default_value); + zend_string *property_prettyPrint_name = zend_string_init("prettyPrint", sizeof("prettyPrint") - 1, 1); + zend_declare_typed_property(class_entry, property_prettyPrint_name, &property_prettyPrint_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_BOOL)); + zend_string_release(property_prettyPrint_name); + + zval property_indentTabs_default_value; + ZVAL_UNDEF(&property_indentTabs_default_value); + zend_string *property_indentTabs_name = zend_string_init("indentTabs", sizeof("indentTabs") - 1, 1); + zend_declare_typed_property(class_entry, property_indentTabs_name, &property_indentTabs_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_BOOL)); + zend_string_release(property_indentTabs_name); + + zval property_indentSize_default_value; + ZVAL_UNDEF(&property_indentSize_default_value); + zend_string *property_indentSize_name = zend_string_init("indentSize", sizeof("indentSize") - 1, 1); + zend_declare_typed_property(class_entry, property_indentSize_name, &property_indentSize_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(property_indentSize_name); + + zval property_smallContainersInline_default_value; + ZVAL_UNDEF(&property_smallContainersInline_default_value); + zend_string *property_smallContainersInline_name = zend_string_init("smallContainersInline", sizeof("smallContainersInline") - 1, 1); + zend_declare_typed_property(class_entry, property_smallContainersInline_name, &property_smallContainersInline_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_BOOL)); + zend_string_release(property_smallContainersInline_name); + + zval property_suppressSystemValues_default_value; + ZVAL_UNDEF(&property_suppressSystemValues_default_value); + zend_string *property_suppressSystemValues_name = zend_string_init("suppressSystemValues", sizeof("suppressSystemValues") - 1, 1); + zend_declare_typed_property(class_entry, property_suppressSystemValues_name, &property_suppressSystemValues_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_BOOL)); + zend_string_release(property_suppressSystemValues_name); + + zval property_flushEveryValue_default_value; + ZVAL_UNDEF(&property_flushEveryValue_default_value); + zend_string *property_flushEveryValue_name = zend_string_init("flushEveryValue", sizeof("flushEveryValue") - 1, 1); + zend_declare_typed_property(class_entry, property_flushEveryValue_name, &property_flushEveryValue_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_BOOL)); + zend_string_release(property_flushEveryValue_name); + + zval property_maxContainerDepth_default_value; + ZVAL_UNDEF(&property_maxContainerDepth_default_value); + zend_string *property_maxContainerDepth_name = zend_string_init("maxContainerDepth", sizeof("maxContainerDepth") - 1, 1); + zend_declare_typed_property(class_entry, property_maxContainerDepth_name, &property_maxContainerDepth_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(property_maxContainerDepth_name); + + zval property_maxAnnotations_default_value; + ZVAL_UNDEF(&property_maxAnnotations_default_value); + zend_string *property_maxAnnotations_name = zend_string_init("maxAnnotations", sizeof("maxAnnotations") - 1, 1); + zend_declare_typed_property(class_entry, property_maxAnnotations_name, &property_maxAnnotations_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(property_maxAnnotations_name); + + zval property_tempBufferSize_default_value; + ZVAL_UNDEF(&property_tempBufferSize_default_value); + zend_string *property_tempBufferSize_name = zend_string_init("tempBufferSize", sizeof("tempBufferSize") - 1, 1); + zend_declare_typed_property(class_entry, property_tempBufferSize_name, &property_tempBufferSize_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(property_tempBufferSize_name); + + zval property_allocationPageSize_default_value; + ZVAL_UNDEF(&property_allocationPageSize_default_value); + zend_string *property_allocationPageSize_name = zend_string_init("allocationPageSize", sizeof("allocationPageSize") - 1, 1); + zend_declare_typed_property(class_entry, property_allocationPageSize_name, &property_allocationPageSize_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(property_allocationPageSize_name); + + return class_entry; +} + +static zend_class_entry *register_class_ion_Writer(void) +{ + zend_class_entry ce, *class_entry; + + INIT_NS_CLASS_ENTRY(ce, "ion", "Writer", class_ion_Writer_methods); + class_entry = zend_register_internal_interface(&ce); + + return class_entry; +} + +static zend_class_entry *register_class_ion_Writer_Writer(zend_class_entry *class_entry_ion_Writer) +{ + zend_class_entry ce, *class_entry; + + INIT_NS_CLASS_ENTRY(ce, "ion\\Writer", "Writer", class_ion_Writer_Writer_methods); + class_entry = zend_register_internal_class_ex(&ce, NULL); + class_entry->ce_flags |= ZEND_ACC_ABSTRACT; + zend_class_implements(class_entry, 1, class_entry_ion_Writer); + + return class_entry; +} + +static zend_class_entry *register_class_ion_Writer_Buffer(zend_class_entry *class_entry_ion_Writer) +{ + zend_class_entry ce, *class_entry; + + INIT_NS_CLASS_ENTRY(ce, "ion\\Writer", "Buffer", class_ion_Writer_Buffer_methods); + class_entry = zend_register_internal_interface(&ce); + zend_class_implements(class_entry, 1, class_entry_ion_Writer); + + return class_entry; +} + +static zend_class_entry *register_class_ion_Writer_Buffer_Writer(zend_class_entry *class_entry_ion_Writer_Writer, zend_class_entry *class_entry_ion_Writer_Buffer) +{ + zend_class_entry ce, *class_entry; + + INIT_NS_CLASS_ENTRY(ce, "ion\\Writer\\Buffer", "Writer", class_ion_Writer_Buffer_Writer_methods); + class_entry = zend_register_internal_class_ex(&ce, class_entry_ion_Writer_Writer); + zend_class_implements(class_entry, 1, class_entry_ion_Writer_Buffer); + + return class_entry; +} + +static zend_class_entry *register_class_ion_Writer_Stream(zend_class_entry *class_entry_ion_Writer) +{ + zend_class_entry ce, *class_entry; + + INIT_NS_CLASS_ENTRY(ce, "ion\\Writer", "Stream", class_ion_Writer_Stream_methods); + class_entry = zend_register_internal_interface(&ce); + zend_class_implements(class_entry, 1, class_entry_ion_Writer); + + return class_entry; +} + +static zend_class_entry *register_class_ion_Writer_Stream_Writer(zend_class_entry *class_entry_ion_Writer_Writer, zend_class_entry *class_entry_ion_Writer_Stream) +{ + zend_class_entry ce, *class_entry; + + INIT_NS_CLASS_ENTRY(ce, "ion\\Writer\\Stream", "Writer", class_ion_Writer_Stream_Writer_methods); + class_entry = zend_register_internal_class_ex(&ce, class_entry_ion_Writer_Writer); + zend_class_implements(class_entry, 1, class_entry_ion_Writer_Stream); + + return class_entry; +} + +static zend_class_entry *register_class_ion_PHP_Serializer(void) +{ + zend_class_entry ce, *class_entry; + + INIT_NS_CLASS_ENTRY(ce, "ion\\PHP", "Serializer", class_ion_PHP_Serializer_methods); + class_entry = zend_register_internal_class_ex(&ce, NULL); + + zend_string *property_writer_class_ion_Writer = zend_string_init("ion\\Writer", sizeof("ion\\Writer")-1, 1); + zval property_writer_default_value; + ZVAL_UNDEF(&property_writer_default_value); + zend_string *property_writer_name = zend_string_init("writer", sizeof("writer") - 1, 1); + zend_declare_typed_property(class_entry, property_writer_name, &property_writer_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_writer_class_ion_Writer, 0, 0)); + zend_string_release(property_writer_name); + + zval property_callMagicSerialize_default_value; + ZVAL_UNDEF(&property_callMagicSerialize_default_value); + zend_string *property_callMagicSerialize_name = zend_string_init("callMagicSerialize", sizeof("callMagicSerialize") - 1, 1); + zend_declare_typed_property(class_entry, property_callMagicSerialize_name, &property_callMagicSerialize_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_BOOL)); + zend_string_release(property_callMagicSerialize_name); + + zval property_callCustomSerialize_default_value; + ZVAL_UNDEF(&property_callCustomSerialize_default_value); + zend_string *property_callCustomSerialize_name = zend_string_init("callCustomSerialize", sizeof("callCustomSerialize") - 1, 1); + zend_declare_typed_property(class_entry, property_callCustomSerialize_name, &property_callCustomSerialize_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING|MAY_BE_NULL)); + zend_string_release(property_callCustomSerialize_name); + + return class_entry; +} + +static zend_class_entry *register_class_ion_PHP_Unserializer(void) +{ + zend_class_entry ce, *class_entry; + + INIT_NS_CLASS_ENTRY(ce, "ion\\PHP", "Unserializer", class_ion_PHP_Unserializer_methods); + class_entry = zend_register_internal_class_ex(&ce, NULL); + + zend_string *property_reader_class_ion_Reader = zend_string_init("ion\\Reader", sizeof("ion\\Reader")-1, 1); + zval property_reader_default_value; + ZVAL_UNDEF(&property_reader_default_value); + zend_string *property_reader_name = zend_string_init("reader", sizeof("reader") - 1, 1); + zend_declare_typed_property(class_entry, property_reader_name, &property_reader_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_reader_class_ion_Reader, 0, 0)); + zend_string_release(property_reader_name); + + zval property_callMagicUnserialize_default_value; + ZVAL_UNDEF(&property_callMagicUnserialize_default_value); + zend_string *property_callMagicUnserialize_name = zend_string_init("callMagicUnserialize", sizeof("callMagicUnserialize") - 1, 1); + zend_declare_typed_property(class_entry, property_callMagicUnserialize_name, &property_callMagicUnserialize_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_BOOL)); + zend_string_release(property_callMagicUnserialize_name); + + zval property_callCustomUnserialize_default_value; + ZVAL_UNDEF(&property_callCustomUnserialize_default_value); + zend_string *property_callCustomUnserialize_name = zend_string_init("callCustomUnserialize", sizeof("callCustomUnserialize") - 1, 1); + zend_declare_typed_property(class_entry, property_callCustomUnserialize_name, &property_callCustomUnserialize_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING|MAY_BE_NULL)); + zend_string_release(property_callCustomUnserialize_name); + + return class_entry; +} diff --git a/ion_private.h b/ion_private.h new file mode 100644 index 0000000..0192e14 --- /dev/null +++ b/ion_private.h @@ -0,0 +1,1486 @@ +/* + +--------------------------------------------------------------------+ + | PECL :: ion | + +--------------------------------------------------------------------+ + | Redistribution and use in source and binary forms, with or without | + | modification, are permitted provided that the conditions mentioned | + | in the accompanying LICENSE file are met. | + +--------------------------------------------------------------------+ + | Copyright (c) 2021, Michael Wallner | + +--------------------------------------------------------------------+ +*/ + +#include "ionc/ion.h" + +#include "php.h" +#include "ext/date/php_date.h" + +typedef struct php_ion_global_serializer php_ion_global_serializer; +typedef struct php_ion_global_unserializer php_ion_global_unserializer; + +ZEND_BEGIN_MODULE_GLOBALS(ion) + + struct php_ion_global_serializer { + HashTable ids; + HashTable tmp; + uint64_t level; + } serializer; + + struct php_ion_global_unserializer { + HashTable ids; + HashTable tmp; + uint64_t level; + } unserializer; + +ZEND_END_MODULE_GLOBALS(ion); + +#ifdef ZTS +# define php_ion_globals (*((zend_ion_globals *) (*((void ***) tsrm_get_ls_cache()))[TSRM_UNSHUFFLE_RSRC_ID(ion_globals_id)])) +#else +# define php_ion_globals ion_globals +#endif + +ZEND_DECLARE_MODULE_GLOBALS(ion); + +static inline void php_ion_globals_serializer_init(void) +{ + php_ion_global_serializer *s = &php_ion_globals.serializer; + + zend_hash_init(&s->tmp, 0, NULL, ZVAL_PTR_DTOR, 0); + zend_hash_init(&s->ids, 0, NULL, NULL, 0); +} + +static inline void php_ion_globals_serializer_step(void) +{ + php_ion_global_serializer *s = &php_ion_globals.serializer; + + if (!s->level++) { + zend_hash_clean(&s->ids); + zend_hash_clean(&s->tmp); + } +} + +static inline void php_ion_globals_serializer_exit(void) +{ + php_ion_global_serializer *s = &php_ion_globals.serializer; + + ZEND_ASSERT(s->level); + if (!--s->level) { + zend_hash_clean(&s->ids); + zend_hash_clean(&s->tmp); + } +} + +static inline void php_ion_globals_serializer_dtor(void) +{ + php_ion_global_serializer *s = &php_ion_globals.serializer; + + zend_hash_destroy(&s->tmp); + zend_hash_destroy(&s->ids); +} + +static inline void php_ion_globals_unserializer_init(void) +{ + php_ion_global_unserializer *s = &php_ion_globals.unserializer; + + zend_hash_init(&s->tmp, 0, NULL, ZVAL_PTR_DTOR, 0); + zend_hash_init(&s->ids, 0, NULL, NULL, 0); +} + +static inline void php_ion_globals_unserializer_step(void) +{ + php_ion_global_unserializer *s = &php_ion_globals.unserializer; + + if (!s->level++) { + zend_hash_clean(&s->ids); + zend_hash_clean(&s->tmp); + } +} + +static inline void php_ion_globals_unserializer_exit(void) +{ + php_ion_global_unserializer *s = &php_ion_globals.unserializer; + + ZEND_ASSERT(s->level); + if (!--s->level) { + zend_hash_clean(&s->ids); + zend_hash_clean(&s->tmp); + } +} + +static inline void php_ion_globals_unserializer_dtor(void) +{ + php_ion_global_unserializer *s = &php_ion_globals.unserializer; + + zend_hash_destroy(&s->tmp); + zend_hash_destroy(&s->ids); +} + +static zend_class_entry + *ce_Annotation, + *ce_Catalog, + *ce_Collection, + *ce_Decimal, + *ce_Decimal_Context, + *ce_PHP_Serializer, + *ce_PHP_Unserializer, + *ce_Reader, + *ce_Reader_Options, + *ce_Reader_Reader, + *ce_Reader_Buffer, + *ce_Reader_Stream, + *ce_Reader_Buffer_Reader, + *ce_Reader_Stream_Reader, + *ce_Symbol, + *ce_Symbol_ImportLocation, + *ce_Symbol_System, + *ce_Symbol_System_SID, + *ce_Symbol_Table, + *ce_Timestamp, + *ce_Type, + *ce_Writer, + *ce_Writer_Options, + *ce_Writer_Buffer, + *ce_Writer_Buffer_Writer, + *ce_Writer_Stream, + *ce_Writer_Stream_Writer, + *ce_Writer_Writer + ; +static zend_object_handlers + oh_catalog, + oh_decimal, + oh_decimal_ctx, + oh_reader_options, + oh_reader, + oh_symbol, + oh_symbol_iloc, + oh_symbol_table, + oh_timestamp, + oh_type, + oh_writer_options, + oh_writer + ; + +#define php_ion_decl(type, name, ...) \ + static zend_object *create_ion_ ## name(zend_class_entry *ce) \ + { \ + if (!ce) ce = ce_ ## name; \ + php_ion_ ## type *o = ecalloc(1, sizeof(*o) + zend_object_properties_size(ce)); \ + zend_object_std_init(&o->std, ce); \ + object_properties_init(&o->std, ce); \ + o->std.handlers = &oh_ ## type; \ + return &o->std; \ + } \ + static void free_ion_ ## name(zend_object *std) \ + { \ + php_ion_ ## type *obj = php_ion_obj(type, std); \ + __VA_ARGS__; \ + zend_object_std_dtor(std); \ + (void) obj; \ + } +#define php_ion_register(type, name, ...) do { \ + ce_ ## name = register_class_ion_ ## name(__VA_ARGS__); \ + ce_ ## name ->create_object = create_ion_ ## name; \ + memcpy(&oh_ ## type, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); \ + oh_ ## type .offset = offsetof(php_ion_ ## type, std); \ + oh_ ## type .free_obj = free_ion_ ## name; \ +} while (0) + +#define php_ion_obj(type, obj) \ + ((php_ion_ ## type *) (obj ? ((char *)(obj) - XtOffsetOf(php_ion_ ## type, std)) : NULL)) + +#define ION_CHECK(err, ...) do { \ + iERR __err = err; \ + if (__err) { \ + zend_throw_exception_ex(spl_ce_RuntimeException, __err, "%s: %s", ion_error_to_str(__err), #err); \ + __VA_ARGS__; \ + return; \ + } \ +} while (0) + +#define ION_CATCH(...) do { \ + if (EG(exception)) { \ + __VA_ARGS__; \ + return; \ + } \ +} while (0) + +#define PTR_CHECK(ptr, ...) do { \ + if (!(ptr)) { \ + zend_throw_error(NULL, "Uninitialized object"); \ + __VA_ARGS__; \ + return; \ + } \ +} while (0) + +#define OBJ_CHECK(obj) do { \ + PTR_CHECK(obj); \ + PTR_CHECK(*((void **)obj)); \ +} while (0) + +static inline ION_STRING *ion_string_from_cstr(ION_STRING *is, const char *s, size_t l) +{ + is->length = l; + is->value = (BYTE *) s; + return is; +} + +static inline ION_STRING *ion_string_from_zend(ION_STRING *is, const zend_string *zs) +{ + is->length = ZSTR_LEN(zs); + is->value = (BYTE *) ZSTR_VAL(zs); + return is; +} + +static inline zend_string *zend_string_from_ion(const ION_STRING *s) +{ + return zend_string_init((const char *) s->value, s->length, 0); +} + +static inline void update_property_obj(zend_object *obj, const char *n, size_t l, zend_object *p) +{ + zval zobj; + ZVAL_OBJ(&zobj, p); + zend_update_property(obj->ce, obj, n, l, &zobj); +} + +typedef struct php_ion_type { + ION_TYPE typ; + zend_object std; +} php_ion_type; + +#define RETURN_IONTYPE(typ) do { \ + zend_object *__zo = php_ion_type_fetch(typ); \ + if (UNEXPECTED(!__zo)) { \ + RETURN_THROWS(); \ + } \ + RETURN_OBJ_COPY(__zo); \ +} while(0) + +static inline zend_object *php_ion_type_fetch(ION_TYPE typ) +{ + zend_long index = ION_TYPE_INT(typ); + zval *ztype = zend_hash_index_find(ce_Type->backed_enum_table, index); + + if (UNEXPECTED(!ztype || Z_TYPE_P(ztype) != IS_STRING)) { + zend_value_error(ZEND_LONG_FMT " is not a valid backing value for enum \"%s\"", index, ZSTR_VAL(ce_Type->name)); + return NULL; + } + return zend_enum_get_case(ce_Type, Z_STR_P(ztype)); +} + +typedef struct php_ion_symbol_iloc { + ION_SYMBOL_IMPORT_LOCATION loc; + zend_string *name; + zend_object std; +} php_ion_symbol_iloc; + +static inline void php_ion_symbol_iloc_ctor(php_ion_symbol_iloc *obj) +{ + zend_update_property_long(obj->std.ce, &obj->std, ZEND_STRL("location"), obj->loc.location); + zend_update_property_str(obj->std.ce, &obj->std, ZEND_STRL("name"), obj->name); + ion_string_from_zend(&obj->loc.name, obj->name); +} + +static inline void php_ion_symbol_iloc_dtor(php_ion_symbol_iloc *obj) +{ + zend_string_release(obj->name); +} + +typedef struct php_ion_symbol { + ION_SYMBOL sym; + zend_string *value; + zend_object *iloc, std; +} php_ion_symbol; + +static inline void php_ion_symbol_ctor(php_ion_symbol *obj) +{ + zend_update_property_long(obj->std.ce, &obj->std, ZEND_STRL("sid"), + obj->sym.sid); + zend_update_property_str(obj->std.ce, &obj->std, ZEND_STRL("value"), obj->value); + ion_string_from_zend(&obj->sym.value, obj->value); + if (obj->iloc) { + update_property_obj(&obj->std, ZEND_STRL("importLocation"), obj->iloc); + obj->sym.import_location = php_ion_obj(symbol_iloc, obj->iloc)->loc; + } else { + zend_update_property_null(obj->std.ce, &obj->std, ZEND_STRL("importLocation")); + } +} + +static inline void php_ion_symbol_dtor(php_ion_symbol *obj) +{ + zend_string_release(obj->value); +} + +static inline void php_ion_symbol_zval(ION_SYMBOL *sym_ptr, zval *return_value) +{ + object_init_ex(return_value, ce_Symbol); + php_ion_symbol *sym = php_ion_obj(symbol, Z_OBJ_P(return_value)); + + sym->sym.sid = sym_ptr->sid; + sym->value = zend_string_from_ion(&sym_ptr->value); + if (!ION_SYMBOL_IMPORT_LOCATION_IS_NULL(sym_ptr)) { + zval ziloc; + object_init_ex(&ziloc, ce_Symbol_ImportLocation); + sym->iloc = Z_OBJ(ziloc); + + php_ion_symbol_iloc *iloc = php_ion_obj(symbol_iloc, sym->iloc); + iloc->loc.location = sym_ptr->import_location.location; + iloc->name = zend_string_from_ion(&sym_ptr->import_location.name); + + php_ion_symbol_iloc_ctor(iloc); + } + + php_ion_symbol_ctor(sym); +} + +typedef struct php_ion_symbol_table { + zend_object std; +} php_ion_symbol_table; + +typedef struct php_ion_decimal_ctx { + decContext ctx; + zend_object std; +} php_ion_decimal_ctx; + +typedef struct php_ion_decimal { + ION_DECIMAL dec; + zend_object *ctx, std; +} php_ion_decimal; + +static inline void php_ion_decimal_ctor(php_ion_decimal *obj) +{ + if (obj->ctx) { + update_property_obj(&obj->std, ZEND_STRL("context"), obj->ctx); + } else { + zend_update_property_null(obj->std.ce, &obj->std, ZEND_STRL("context")); + } +} + +static inline void php_ion_decimal_dtor(php_ion_decimal *obj) +{ + ion_decimal_free(&obj->dec); +} + +static inline zend_string *php_ion_decimal_to_string(ION_DECIMAL *dec) +{ + zend_string *zstr = zend_string_alloc(ION_DECIMAL_STRLEN(dec), 0); + (void) ion_decimal_to_string(dec, zstr->val); + return zend_string_truncate(zstr, strlen(zstr->val), 0); +} + +static zend_array *php_ion_decimal_get_props_for(zend_object *object, zend_prop_purpose purpose) +{ + php_ion_decimal *obj = php_ion_obj(decimal, object); + + switch (purpose) { + case ZEND_PROP_PURPOSE_DEBUG: + zend_array *arr = zend_new_array(1); + zval *zv = zend_hash_str_add_empty_element(arr, ZEND_STRL("value")); + ZVAL_STR(zv, php_ion_decimal_to_string(&obj->dec)); + return arr; + + default: + return zend_std_get_properties_for(object, purpose); + } +} + +typedef php_date_obj php_ion_timestamp; + +static inline void php_ion_timestamp_ctor(php_ion_timestamp *obj, zend_long precision, zend_string *fmt, zend_string *dt, zval *tz) +{ + if (!obj->time) { + php_date_initialize(obj, dt ? dt->val : "now", dt ? dt->len : 3, fmt ? fmt->val : NULL, tz, PHP_DATE_INIT_CTOR); + } + zend_update_property_long(obj->std.ce, &obj->std, ZEND_STRL("precision"), precision); + if (fmt) { + zend_update_property_str(obj->std.ce, &obj->std, ZEND_STRL("format"), fmt); + } else { + zend_update_property_stringl(obj->std.ce, &obj->std, ZEND_STRL("format"), ZEND_STRL("c")); + } +} + +static inline void php_ion_timestamp_dtor(php_ion_timestamp *obj) +{ + if (obj->time) { + timelib_time_dtor(obj->time); + } +} + +static inline zend_long php_usec_from_ion(const decQuad *frac, decContext *ctx) +{ + decQuad microsecs, result; + decQuadMultiply(&result, decQuadFromInt32(µsecs, 1000000), frac, ctx); + return (zend_long) decQuadToUInt32(&result, ctx, DEC_ROUND_HALF_EVEN); +} + +static inline decQuad *ion_ts_frac_from_usec(decQuad *frac, zend_long usec, decContext *ctx) +{ + decQuad microsecs, us; + return decQuadDivide(frac, decQuadFromInt32(&us, usec), decQuadFromInt32(µsecs, 1000000), ctx); +} + +static inline timelib_time* php_time_from_ion(const ION_TIMESTAMP *ts, decContext *ctx, zend_string **fmt) +{ + timelib_time *time = timelib_time_ctor(); + + switch (ts->precision) { + case ION_TS_FRAC: + time->us = php_usec_from_ion(&ts->fraction, ctx); + if (fmt) *fmt = zend_string_init(ZEND_STRL("c"), 0); + /* fallthrough */ + case ION_TS_SEC: + time->s = ts->seconds; + if (fmt && !*fmt) *fmt = zend_string_init(ZEND_STRL("Y-m-d\\TH:i:sP"), 0); + /* fallthrough */ + case ION_TS_MIN: + time->i = ts->minutes; + time->h = ts->hours; + if (fmt && !*fmt) *fmt = zend_string_init(ZEND_STRL("Y-m-d\\TH:iP"), 0); + /* fallthrough */ + case ION_TS_DAY: + time->d = ts->day; + if (fmt && !*fmt) *fmt = zend_string_init(ZEND_STRL("Y-m-d\\T"), 0); + /* fallthrough */ + case ION_TS_MONTH: + time->m = ts->month; + if (fmt && !*fmt) *fmt = zend_string_init(ZEND_STRL("Y-m\\T"), 0); + /* fallthrough */ + case ION_TS_YEAR: + time->y = ts->year; + if (fmt && !*fmt) *fmt = zend_string_init(ZEND_STRL("Y\\T"), 0); + /* fallthrough */ + default: + if (fmt && !*fmt) *fmt = zend_string_init(ZEND_STRL("c"), 0); + time->z = ts->tz_offset * 60; + } + + return time; +} + +static inline ION_TIMESTAMP *ion_timestamp_from_php(ION_TIMESTAMP *buf, php_ion_timestamp *ts, decContext *ctx) +{ + memset(buf, 0, sizeof(*buf)); + + zval tmp; + uint8_t precision = Z_LVAL_P(zend_read_property(ts->std.ce, &ts->std, ZEND_STRL("precision"), 0, &tmp)); + + if (!precision || precision > ION_TS_FRAC) { + zend_throw_exception_ex(spl_ce_InvalidArgumentException, IERR_INVALID_ARG, + "Invalid precision (%u) of ion\\Timestamp", (unsigned) precision); + } else switch ((buf->precision = precision)) { + case ION_TS_FRAC: + ion_ts_frac_from_usec(&buf->fraction, ts->time->us, ctx); + /* fallthrough */ + case ION_TS_SEC: + buf->seconds = ts->time->s; + /* fallthrough */ + case ION_TS_MIN: + buf->minutes = ts->time->i; + /* fallthrough */ + case ION_TS_DAY: + buf->hours = ts->time->h; + buf->day = ts->time->d; + /* fallthrough */ + case ION_TS_MONTH: + buf->month = ts->time->m; + /* fallthrough */ + case ION_TS_YEAR: + buf->year = ts->time->y; + /* fallthrough */ + default: + buf->tz_offset = ts->time->z / 60; + } + + return buf; +} + +typedef struct php_ion_catalog { + ION_CATALOG *cat; + zend_object std; +} php_ion_catalog; + +typedef struct php_ion_reader_options { + ION_READER_OPTIONS opt; + zend_object *cat, *dec_ctx, *cb, std; +} php_ion_reader_options; + +typedef struct php_ion_reader { + ION_READER *reader; + ION_TYPE state; + enum { + BUFFER_READER, + STREAM_READER, + } type; + union { + zend_string *buffer; + struct { + php_stream *ptr; + ION_STRING buf; + } stream; + }; + struct { + zend_bool call_magic_unserialize; + zend_string *custom_unserialize; + } php; + zend_object *opt, std; +} php_ion_reader; + +static iERR php_ion_reader_stream_handler(struct _ion_user_stream *user) +{ + php_ion_reader *reader = (php_ion_reader *) user->handler_state; + size_t remaining = 0, spare = reader->stream.buf.length; + + if (user->curr && user->limit && (remaining = user->limit - user->curr)) { + memmove(reader->stream.buf.value, user->curr, remaining); + user->limit -= remaining; + spare -= remaining; + } else { + user->curr = user->limit = reader->stream.buf.value; + } + + ssize_t read = php_stream_read(reader->stream.ptr, (char *) user->limit, spare); + if (EXPECTED(read > 0)) { + user->limit += read; + return IERR_OK; + } + + if (EXPECTED(read == 0)) { + return IERR_EOF; + } + + return IERR_READ_ERROR; +} + +static iERR on_context_change(void *context, ION_COLLECTION *imports) +{ + if (context) { + php_ion_reader *obj = php_ion_obj(reader, context); + (void) obj; + } + return IERR_OK; +} + +static ION_READER_CONTEXT_CHANGE_NOTIFIER EMPTY_READER_CHANGE_NOTIFIER = { + on_context_change, + NULL +}; + +static inline void php_ion_reader_ctor(php_ion_reader *obj) +{ + iERR err; + php_ion_reader_options *opt = php_ion_obj(reader_options, obj->opt); + + if (opt) { + opt->opt.context_change_notifier.context = obj; + } + if (obj->type == STREAM_READER) { + PTR_CHECK(obj->stream.ptr); + GC_ADDREF(obj->stream.ptr->res); + + php_ion_reader_options *opt = php_ion_obj(reader_options, obj->opt); + obj->stream.buf.length = opt ? opt->opt.allocation_page_size : 0x1000; + obj->stream.buf.value = emalloc(obj->stream.buf.length); + err = ion_reader_open_stream(&obj->reader, obj, php_ion_reader_stream_handler, opt ? &opt->opt : NULL); + + } else { + err = ion_reader_open_buffer(&obj->reader, + (BYTE *) obj->buffer->val, obj->buffer->len, + opt ? &opt->opt : NULL); + } + if (opt) { + opt->opt.context_change_notifier.context = NULL; + } + + ION_CHECK(err); + OBJ_CHECK(obj); +} +static inline void php_ion_reader_dtor(php_ion_reader *obj) +{ + if (obj->reader) { + ion_reader_close(obj->reader); + } + if (obj->type == STREAM_READER) { + if (obj->stream.buf.value) { + efree(obj->stream.buf.value); + } + if (obj->stream.ptr) { + zend_list_delete(obj->stream.ptr->res); + } + } else { + if (obj->buffer) { + zend_string_release(obj->buffer); + } + } +} + +typedef struct php_ion_writer_options { + ION_WRITER_OPTIONS opt; + zend_object *cat, *dec_ctx, *col, std; +} php_ion_writer_options; + +typedef struct php_ion_writer { + ION_WRITER *writer; + enum { + BUFFER_WRITER, + STREAM_WRITER, + } type; + union { + struct { + zval val; + smart_str str; + } buffer; + struct { + ION_STRING buf; + php_stream *ptr; + } stream; + }; + struct { + zend_bool call_magic_serialize; + zend_string *custom_serialize; + } php; + zend_object *opt, std; + +} php_ion_writer; + +static iERR php_ion_writer_stream_handler(struct _ion_user_stream *user) +{ + php_ion_writer *writer = (php_ion_writer *) user->handler_state; + + if (EXPECTED(user->limit && user->curr)) { + ptrdiff_t len = user->curr - writer->stream.buf.value; + if (len != php_stream_write(writer->stream.ptr, (char *) writer->stream.buf.value, len)) { + return IERR_WRITE_ERROR; + } + } + user->curr = writer->stream.buf.value; + user->limit = writer->stream.buf.value + writer->stream.buf.length; + return IERR_OK; +} + +#define REF_STR() do { \ + ZVAL_NEW_STR(ref, obj->buffer.str.s); \ + GC_ADDREF(obj->buffer.str.s); \ +} while (0) + +#define NEW_REF_STR() do {\ + if (Z_STR_P(ref) != obj->buffer.str.s) { \ + zval_ptr_dtor(ref); \ + REF_STR(); \ + } \ +} while(0) + +static inline void php_ion_writer_stream_init(php_ion_writer *obj, php_ion_writer_options *opt) +{ + PTR_CHECK(obj->stream.ptr); + GC_ADDREF(obj->stream.ptr->res); + + obj->stream.buf.length = opt ? opt->opt.allocation_page_size : 0x1000; + obj->stream.buf.value = emalloc(obj->stream.buf.length); +} +static inline void php_ion_writer_buffer_init(php_ion_writer *obj) +{ + zval *ref = &obj->buffer.val; + ZVAL_DEREF(ref); + + smart_str_alloc(&obj->buffer.str, 0, 0); + smart_str_0(&obj->buffer.str); + REF_STR(); +} + +static inline void php_ion_writer_buffer_grow(php_ion_writer *obj) +{ + zval *ref = &obj->buffer.val; + ZVAL_DEREF(ref); + + switch (GC_REFCOUNT(obj->buffer.str.s)) { + case 2: + // nothing to do + break; + case 1: + // we've been separated + GC_ADDREF(obj->buffer.str.s); + break; + default: + // we have to separate + fprintf(stderr, "SEPARATE\n"); + obj->buffer.str.s = zend_string_dup(obj->buffer.str.s, 0); + break; + } + + zend_string *old = obj->buffer.str.s; + GC_DELREF(old); + smart_str_erealloc(&obj->buffer.str, obj->buffer.str.a << 1); + if (old == obj->buffer.str.s) { + GC_ADDREF(old); + } else if(old == Z_STR_P(ref)) { + ZVAL_NULL(ref); + } + + NEW_REF_STR(); +} + +static iERR php_ion_writer_buffer_handler(struct _ion_user_stream *user) +{ + php_ion_writer *writer = (php_ion_writer *) user->handler_state; + + if (user->curr) { + writer->buffer.str.s->len += user->curr - (BYTE *) &writer->buffer.str.s->val[writer->buffer.str.s->len]; + smart_str_0(&writer->buffer.str); + if (user->limit == user->curr) { + php_ion_writer_buffer_grow(writer); + } + } + user->curr = (BYTE *) &writer->buffer.str.s->val[writer->buffer.str.s->len]; + user->limit = user->curr + writer->buffer.str.a - writer->buffer.str.s->len; + + return IERR_OK; +} + +static inline void php_ion_writer_ctor(php_ion_writer *obj) +{ + if (obj->opt) { + update_property_obj(&obj->std, ZEND_STRL("options"), obj->opt); + } + + php_ion_writer_options *opt = php_ion_obj(writer_options, obj->opt); + ION_STREAM_HANDLER h; + if (obj->type == STREAM_WRITER) { + h = php_ion_writer_stream_handler; + php_ion_writer_stream_init(obj, opt); + } else { + h = php_ion_writer_buffer_handler; + php_ion_writer_buffer_init(obj); + } + + ION_CHECK(ion_writer_open_stream(&obj->writer, h, obj, opt ? &opt->opt : NULL)); + OBJ_CHECK(obj); +} + +static inline void php_ion_writer_dtor(php_ion_writer *obj) +{ + if (obj->writer) { + ion_writer_close(obj->writer); + } + if (obj->type == STREAM_WRITER) { + if (obj->stream.buf.value) { + efree(obj->stream.buf.value); + } + if (obj->stream.ptr) { + zend_list_delete(obj->stream.ptr->res); + } + } else { + smart_str_0(&obj->buffer.str); + zend_string_release(obj->buffer.str.s); + zval_ptr_dtor(&obj->buffer.val); + } + if (obj->php.custom_serialize) { + zend_string_release(obj->php.custom_serialize); + } +} + +static inline void php_ion_serialize_zval(php_ion_writer *, zval *); + +static inline void php_ion_serialize_struct(php_ion_writer *obj, zend_array *arr) +{ + ION_CHECK(ion_writer_start_container(obj->writer, tid_STRUCT)); + + zval *v; + zend_ulong h; + zend_string *k = NULL; + if (arr) ZEND_HASH_FOREACH_KEY_VAL_IND(arr, h, k, v) + ION_STRING is; + if (k) { + ION_CHECK(ion_writer_write_field_name(obj->writer, ion_string_from_zend(&is, k))); + } else { + char buf[MAX_LENGTH_OF_LONG + 1], *ptr = zend_print_ulong_to_buf(buf + sizeof(buf), h); + ION_CHECK(ion_writer_add_annotation(obj->writer, ion_string_from_cstr(&is, ZEND_STRL("i")))); + ION_CHECK(ion_writer_write_field_name(obj->writer, ion_string_from_cstr(&is, ptr, buf - ptr))); + } + + php_ion_serialize_zval(obj, v); + ION_CATCH(); + ZEND_HASH_FOREACH_END(); + + ION_CHECK(ion_writer_finish_container(obj->writer)); +} + +static inline void php_ion_serialize_list(php_ion_writer *obj, zend_array *arr) +{ + ION_CHECK(ion_writer_start_container(obj->writer, tid_LIST)); + + zval *v; + ZEND_HASH_FOREACH_VAL_IND(arr, v) + php_ion_serialize_zval(obj, v); + ION_CATCH(); + ZEND_HASH_FOREACH_END(); + + ION_CHECK(ion_writer_finish_container(obj->writer)); +} + +static inline void php_ion_serialize_array(php_ion_writer *obj, zend_array *arr) +{ + if (zend_array_is_list(arr)) { + php_ion_serialize_list(obj, arr); + } else { + php_ion_serialize_struct(obj, arr); + } +} + +static inline void php_ion_serialize_object_iface(php_ion_writer *obj, zend_object *zobject) +{ + uint8_t *buf; + size_t len; + zval tmp; + + ZVAL_OBJ(&tmp, zobject); + if (SUCCESS == zobject->ce->serialize(&tmp, &buf, &len, NULL)) { + ION_STRING is; + ION_CHECK(ion_writer_add_annotation(obj->writer, ion_string_from_cstr(&is, ZEND_STRL("S")))); + ION_CHECK(ion_writer_add_annotation(obj->writer, ion_string_from_zend(&is, zobject->ce->name))); + ION_CHECK(ion_writer_write_string(obj->writer, ion_string_from_cstr(&is, (char *) buf, len))); + efree(buf); + } else if (!EG(exception)){ + zend_throw_exception_ex(spl_ce_UnexpectedValueException, IERR_INTERNAL_ERROR, + "Failed to serialize class %s", zobject->ce->name->val); + } +} + +static inline void php_ion_serialize_object_magic(php_ion_writer *obj, zend_object *zobject, zend_function *fn) +{ + zval rv; + + ZVAL_NULL(&rv); + zend_call_method_with_0_params(zobject, zobject->ce, fn ? &fn : &zobject->ce->__serialize, "", &rv); + ION_CATCH(); + + if (IS_ARRAY == Z_TYPE(rv)) { + ION_STRING is; + ION_CHECK(ion_writer_add_annotation(obj->writer, ion_string_from_cstr(&is, ZEND_STRL(fn ? "C" : "O")))); + ION_CHECK(ion_writer_add_annotation(obj->writer, ion_string_from_zend(&is, zobject->ce->name))); + php_ion_serialize_zval(obj, &rv); + zval_ptr_dtor(&rv); + } else { + zend_throw_exception_ex(spl_ce_UnexpectedValueException, IERR_INTERNAL_ERROR, + "%s serializer %s::%s did not return an array", + fn ? "Custom" : "Magic", zobject->ce->name->val, + fn ? fn->common.function_name->val : "__serialize"); + } +} + +static inline zend_string *fq_enum_case(zend_object *zobject) +{ + zval *cn = zend_enum_fetch_case_name(zobject); + zend_string *en = zend_string_alloc(zobject->ce->name->len + Z_STRLEN_P(cn) + strlen("\\"), 0); + memcpy(en->val, zobject->ce->name->val, zobject->ce->name->len); + en->val[zobject->ce->name->len] = '\\'; + memcpy(&en->val[zobject->ce->name->len + 1], Z_STRVAL_P(cn), Z_STRLEN_P(cn)); + en->val[en->len] = 0; + return en; +} + +static inline void php_ion_serialize_object_enum(php_ion_writer *obj, zend_object *zobject) +{ + ION_STRING is; + ION_CHECK(ion_writer_add_annotation(obj->writer, ion_string_from_cstr(&is, ZEND_STRL("E")))); + + zend_string *en = fq_enum_case(zobject); + ION_CHECK(ion_writer_write_string(obj->writer, ion_string_from_zend(&is, en))); + zend_string_release(en); +} + +static inline void php_ion_serialize_object_std(php_ion_writer *obj, zend_object *zobject) +{ + ION_STRING is; + ION_CHECK(ion_writer_add_annotation(obj->writer, ion_string_from_cstr(&is, ZEND_STRL("o")))); + + if (zobject->ce != zend_standard_class_def) { + ION_CHECK(ion_writer_add_annotation(obj->writer, ion_string_from_zend(&is, zobject->ce->name))); + } + + zval zobj; + ZVAL_OBJ(&zobj, zobject); + HashTable *props = zend_get_properties_for(&zobj, ZEND_PROP_PURPOSE_SERIALIZE); + if (props) { + php_ion_serialize_struct(obj, props); + zend_release_properties(props); + } else { + zend_throw_exception_ex(spl_ce_UnexpectedValueException, IERR_INTERNAL_ERROR, + "Could not get properties for serialization of class %s", + zobject->ce->name->val); + } +} + +static inline bool can_call_magic_serialize(php_ion_writer *obj, zend_class_entry *ce) +{ + if (ce->__serialize && obj->php.call_magic_serialize) { + return true; + } + return false; +} + +static inline bool can_call_iface_serialize(php_ion_writer *obj, zend_class_entry *ce) +{ + return !!ce->serialize; +} + +static inline bool can_call_custom_serialize(php_ion_writer *obj, zend_object *zobject, zend_function **fn) +{ + if (obj->php.custom_serialize) { + return !!((*fn = zend_hash_find_ptr(&zobject->ce->function_table, obj->php.custom_serialize))); + } + return false; +} + +static inline void php_ion_serialize_object(php_ion_writer *obj, zend_object *zobject) +{ + zend_function *fn; + zend_class_entry *ce = zobject->ce; + ZEND_ASSERT(ce); + + if (ce->ce_flags & ZEND_ACC_NOT_SERIALIZABLE) { + zend_throw_exception_ex(spl_ce_InvalidArgumentException, IERR_INVALID_ARG, + "Serializing %s is not allowed", ce->name->val); + return; + } + + if (can_call_magic_serialize(obj, ce)) { + php_ion_serialize_object_magic(obj, zobject, NULL); + } else if (can_call_iface_serialize(obj, ce)) { + php_ion_serialize_object_iface(obj, zobject); + } else if (can_call_custom_serialize(obj, zobject, &fn)) { + php_ion_serialize_object_magic(obj, zobject, fn); + } else if (zobject->ce->ce_flags & ZEND_ACC_ENUM) { + php_ion_serialize_object_enum(obj, zobject); + } else if (ce == ce_Symbol) { + ION_CHECK(ion_writer_write_ion_symbol(obj->writer, &php_ion_obj(symbol, zobject)->sym)); + } else if (ce == ce_Decimal) { + ION_CHECK(ion_writer_write_ion_decimal(obj->writer, &php_ion_obj(decimal, zobject)->dec)); + } else if (ce == ce_Timestamp) { + ION_TIMESTAMP its; + php_ion_timestamp *pts = php_ion_obj(timestamp, zobject); + php_ion_writer_options *opt = php_ion_obj(writer_options, obj->opt); + decContext *ctx = opt ? opt->opt.decimal_context : NULL; + ION_CHECK(ion_writer_write_timestamp(obj->writer, ion_timestamp_from_php(&its, pts, ctx))); + } else { + php_ion_serialize_object_std(obj, zobject); + } +} + +static inline void php_ion_serialize_refcounted(php_ion_writer *obj, zval *zv) +{ + zend_ulong idx = (zend_ulong) (uintptr_t) Z_COUNTED_P(zv); + + ION_STRING is; + if (zend_hash_index_exists(&php_ion_globals.serializer.ids, idx)) { + zval *num = zend_hash_index_find(&php_ion_globals.serializer.ids, idx); + + ION_CHECK(ion_writer_add_annotation(obj->writer, ion_string_from_cstr(&is, ZEND_STRL("r")))); + ION_CHECK(ion_writer_write_int64(obj->writer, Z_LVAL_P(num))); + } else { + zval num; + + ZVAL_LONG(&num, zend_hash_num_elements(&php_ion_globals.serializer.ids)); + zend_hash_index_add(&php_ion_globals.serializer.ids, idx, &num); + + Z_TRY_ADDREF_P(zv); + zend_hash_next_index_insert(&php_ion_globals.serializer.tmp, zv); + + switch (Z_TYPE_P(zv)) { + case IS_STRING: + ION_CHECK(ion_writer_write_string(obj->writer, ion_string_from_zend(&is, Z_STR_P(zv)))); + break; + + case IS_ARRAY: + php_ion_serialize_array(obj, Z_ARRVAL_P(zv)); + break; + + case IS_OBJECT: + php_ion_serialize_object(obj, Z_OBJ_P(zv)); + break; + + case IS_REFERENCE: + ION_CHECK(ion_writer_add_annotation(obj->writer, ion_string_from_cstr(&is, ZEND_STRL("R")))); + php_ion_serialize_zval(obj, Z_REFVAL_P(zv)); + break; + } + } +} + +static inline void php_ion_serialize_zval(php_ion_writer *obj, zval *zv) +{ + OBJ_CHECK(obj); + + switch (Z_TYPE_P(zv)) { + case IS_NULL: + ION_CHECK(ion_writer_write_null(obj->writer)); + break; + case IS_TRUE: + ION_CHECK(ion_writer_write_bool(obj->writer, TRUE)); + break; + case IS_FALSE: + ION_CHECK(ion_writer_write_bool(obj->writer, FALSE)); + break; + case IS_LONG: + ION_CHECK(ion_writer_write_int64(obj->writer, Z_LVAL_P(zv))); + break; + case IS_DOUBLE: + ION_CHECK(ion_writer_write_double(obj->writer, Z_DVAL_P(zv))); + break; + case IS_STRING: + case IS_ARRAY: + case IS_OBJECT: + case IS_REFERENCE: + php_ion_serialize_refcounted(obj, zv); + break; + default: + zend_throw_exception_ex(spl_ce_InvalidArgumentException, IERR_INVALID_ARG, + "Failed to serialize value of type %s", zend_zval_get_legacy_type(zv)->val); + } +} + +void php_ion_serialize(php_ion_writer *obj, zval *zv, zval *return_value) +{ + zval zwriter; + + if (obj) { + ZVAL_OBJ_COPY(&zwriter, &obj->std); + } else { + object_init_ex(&zwriter, ce_Writer_Buffer_Writer); + obj = php_ion_obj(writer, Z_OBJ(zwriter)); + obj->type = BUFFER_WRITER; + php_ion_writer_ctor(obj); + } + + php_ion_globals_serializer_step(); + + /* start off with a global PHP annotation instead of repeating it all over the place */ + ION_STRING is; + ION_CHECK(ion_writer_add_annotation(obj->writer, ion_string_from_cstr(&is, ZEND_STRL("PHP")))); + + php_ion_serialize_zval(obj, zv); + + /* make sure to flush when done, else str.s might not contain everything until the writer is closed */ + ion_writer_flush(obj->writer, NULL); + RETVAL_STR_COPY(obj->buffer.str.s); + + php_ion_globals_serializer_exit(); + + zval_ptr_dtor(&zwriter); +} + +static inline void php_ion_unserialize_zval(php_ion_reader *obj, zval *return_value); + +static inline bool can_call_magic_unserialize(php_ion_reader *obj, zend_class_entry *ce) +{ + if (ce->__unserialize && obj->php.call_magic_unserialize) { + return true; + } + return false; +} + +static inline bool can_call_iface_unserialize(php_ion_reader *obj, zend_class_entry *ce) +{ + return !!ce->unserialize; +} + +static inline bool can_call_custom_unserialize(php_ion_reader *obj, zend_object *zobject, zend_function **fn) +{ + if (obj->php.custom_unserialize) { + return !!((*fn = zend_hash_find_ptr(&zobject->ce->function_table, obj->php.custom_unserialize))); + } + return false; +} + +static inline zval *php_ion_unserialize_class(php_ion_reader *obj, zend_string *class_name, zval *return_value) +{ + zend_class_entry *ce = zend_lookup_class(class_name); + + if (ce) { + object_init_ex(return_value, ce); + return zend_hash_next_index_insert(&php_ion_globals.unserializer.ids, return_value); + } + + zend_throw_exception_ex(spl_ce_RuntimeException, IERR_IMPORT_NOT_FOUND, + "Could not find class %s", class_name->val); + return NULL; +} + +static inline void php_ion_unserialize_object_iface(php_ion_reader *obj, zend_string *class_name, zval *return_value) +{ + ZEND_ASSERT(Z_TYPE_P(return_value) == IS_STRING); + zend_string *s = Z_STR_P(return_value); + + zval *backref = php_ion_unserialize_class(obj, class_name, return_value); + ION_CATCH(); + + zend_class_entry *ce = Z_OBJCE_P(return_value); + if (can_call_iface_unserialize(obj, ce)) { + if (SUCCESS == ce->unserialize(backref, ce, (BYTE *) s->val, s->len, NULL)) { + // remove all this Serializable crap in PHP-9 + zval_ptr_dtor(return_value); + ZVAL_COPY_VALUE(return_value, backref); + } else if (!EG(exception)) { + zend_throw_exception_ex(spl_ce_UnexpectedValueException, IERR_INTERNAL_ERROR, + "Failed to unserialize class %s", ce->name->val); + } + } else { + zend_throw_exception_ex(spl_ce_RuntimeException, IERR_INVALID_TOKEN, + "Class %s does not implement Serializable", class_name->val); + } + + zend_string_release(s); +} + +static inline void php_ion_unserialize_hash(php_ion_reader *obj, zval *return_value) +{ + ION_CHECK(ion_reader_step_in(obj->reader)); + + ION_TYPE typ; + while (true) { + ION_CHECK(ion_reader_next(obj->reader, &typ)); + + if (typ == tid_EOF) { + break; + } + + ION_STRING name; + ION_CHECK(ion_reader_get_field_name(obj->reader, &name)); + + zend_string *zkey = zend_string_from_ion(&name); + zval *zvalue = zend_hash_add_empty_element(HASH_OF(return_value), zkey); + php_ion_unserialize_zval(obj, zvalue); + zend_string_release(zkey); + ION_CATCH(); + } + + ION_CHECK(ion_reader_step_out(obj->reader)); +} + +static inline void php_ion_unserialize_object_magic(php_ion_reader *obj, zend_string *class_name, bool custom, zval *return_value) +{ + php_ion_unserialize_class(obj, class_name, return_value); + ION_CATCH(); + + zend_object *zobject = Z_OBJ_P(return_value); + zend_function *fn = NULL; + if (custom) { + if (!can_call_custom_unserialize(obj, Z_OBJ_P(return_value), &fn)) { + zend_throw_exception_ex(spl_ce_RuntimeException, IERR_INVALID_TOKEN, + "Could not find custom serializer method of %s", class_name->val); + return; + } + } else { + if (!can_call_magic_unserialize(obj, zobject->ce)) { + zend_throw_exception_ex(spl_ce_RuntimeException, IERR_INVALID_TOKEN, + "Could not find method %s::__serialize()", class_name->val); + } + } + + zval sv; + array_init(&sv); + php_ion_unserialize_hash(obj, &sv); + ION_CATCH(zval_ptr_dtor(&sv)); + + zval rv; + ZVAL_NULL(&rv); + zend_call_method_with_1_params(zobject, zobject->ce, fn ? &fn : &zobject->ce->__serialize, "", &rv, &sv); + zval_ptr_dtor(&rv); + zval_ptr_dtor(&sv); +} + +static inline void php_ion_unserialize_object_of_class(php_ion_reader *obj, uint8_t object_type, zend_string *class_name, zval *return_value) +{ + switch (object_type) { + case 'S': + php_ion_unserialize_object_iface(obj, class_name, return_value); + break; + + case 'C': + php_ion_unserialize_object_magic(obj, class_name, true, return_value); + break; + + case 'O': + php_ion_unserialize_object_magic(obj, class_name, false, return_value); + break; + + default: + zend_throw_exception_ex(spl_ce_RuntimeException, IERR_INVALID_TOKEN, + "Invalid object type %c", object_type); + } +} + +static inline void php_ion_unserialize_struct(php_ion_reader *obj, uint8_t object_type, zend_string *class_name, zval *return_value) +{ + if (class_name) { + php_ion_unserialize_object_of_class(obj, object_type, class_name, return_value); + } else if (!object_type) { + array_init(return_value); + zend_hash_next_index_insert(&php_ion_globals.unserializer.ids, return_value); + php_ion_unserialize_hash(obj, return_value); + } else if (object_type == 'o') { + object_init(return_value); + zend_hash_next_index_insert(&php_ion_globals.unserializer.ids, return_value); + php_ion_unserialize_hash(obj, return_value); + } else { + zend_throw_exception_ex(spl_ce_RuntimeException, IERR_INVALID_TOKEN, + "Invalid object type %c", object_type); + } +} + +static inline void php_ion_unserialize_list(php_ion_reader *obj, zval *return_value) +{ + ION_CHECK(ion_reader_step_in(obj->reader)); + array_init(return_value); + zend_hash_next_index_insert(&php_ion_globals.unserializer.ids, return_value); + + ION_TYPE typ; + HashTable *ht = Z_ARRVAL_P(return_value); + while (true) { + zval next; + php_ion_unserialize_zval(obj, &next); + ION_CATCH(); + + ION_CHECK(ion_reader_get_type(obj->reader, &typ)); + if (typ == tid_EOF) { + break; + } + + zend_hash_next_index_insert(ht, &next); + } + + ION_CHECK(ion_reader_step_out(obj->reader)); +} + +static inline void php_ion_unserialize_lob(php_ion_reader *obj, zval *return_value) +{ + zend_string *zstr = zend_string_alloc(0x1000, 0); +again: + SIZE read = 0; + iERR err = ion_reader_read_lob_bytes(obj->reader, (BYTE *) zstr->val, zstr->len, &read); + if (err == IERR_BUFFER_TOO_SMALL) { + zstr = zend_string_extend(zstr, zstr->len << 2, 0); + goto again; + } + ION_CHECK(err, zend_string_release(zstr)); + if (zstr->len > read) { + zstr = zend_string_truncate(zstr, read, 0); + } + RETURN_STR(zstr); +} + +static inline void php_ion_unserialize_timestamp(php_ion_reader *obj, zval *return_value) +{ + ION_TIMESTAMP ts; + ION_CHECK(ion_reader_read_timestamp(obj->reader, &ts)); + + decContext *ctx = NULL; + if (obj->opt) { + ctx = php_ion_obj(reader_options, obj->opt)->opt.decimal_context; + } + + object_init_ex(return_value, ce_Timestamp); + php_ion_timestamp *ts_obj = php_ion_obj(timestamp, Z_OBJ_P(return_value)); + + zend_string *fmt = NULL; + ts_obj->time = php_time_from_ion(&ts, ctx, &fmt); + php_ion_timestamp_ctor(ts_obj, ts.precision, fmt, NULL, NULL); + zend_string_release(fmt); + + OBJ_CHECK(ts_obj); +} + +static inline void php_ion_unserialize_int(php_ion_reader *obj, zval *return_value) +{ + ION_INT *num = NULL; + ION_CHECK(ion_int_alloc(obj->reader, &num)); + ION_CHECK(ion_reader_read_ion_int(obj->reader, num)); + + // TODO: SIZEOF_ZEND_LONG == 4 + int64_t i64; + iERR err = ion_int_to_int64(num, &i64); + switch (err) { + case IERR_OK: + RETVAL_LONG(i64); + goto done; + + case IERR_NUMERIC_OVERFLOW: + SIZE max, len; + ION_CHECK(ion_int_char_length(num, &max)); + zend_string *zs = zend_string_alloc(max-1, 0); + + err = ion_int_to_char(num, (BYTE *) zs->val, max, &len); + ZEND_ASSERT(len == zs->len); + RETVAL_STR(zs); + /* fall through */ + + default: + done: + ion_int_free(num); + ION_CHECK(err); + } +} + +static inline void php_ion_unserialize_backref(php_ion_reader *obj, zval *return_value) +{ + php_ion_global_unserializer *u = &php_ion_globals.unserializer; + zval *backref = zend_hash_index_find(&u->ids, Z_LVAL_P(return_value)); + + if (backref) { + ZVAL_COPY(return_value, backref); + } else { + zend_throw_exception_ex(spl_ce_RuntimeException, IERR_INTERNAL_ERROR, + "Could not find backref %ld", Z_LVAL_P(return_value)); + } +} + +static inline void php_ion_unserialize_zval(php_ion_reader *obj, zval *return_value) +{ + ION_TYPE typ; + ION_CHECK(ion_reader_next(obj->reader, &typ)); + +#define next_annotation() do { \ + if (ann_count) { \ + --ann_count; \ + ION_CHECK(ion_reader_get_an_annotation(obj->reader, ann_index++, &annotation)); \ + } \ +} while (0) +#define has_annotation(a) (has_annotations && annotation.length == 1 && annotation.value[0] == a) + + BOOL has_annotations; + int32_t ann_index = 0, ann_count = 0; + ION_STRING annotation = {0}; + ION_CHECK(ion_reader_has_any_annotations(obj->reader, &has_annotations)); + if (has_annotations) { + ION_CHECK(ion_reader_get_annotation_count(obj->reader, &ann_count)); + next_annotation(); + } + + if (has_annotation('r')) { + // BACKREF + php_ion_unserialize_int(obj, return_value); + ION_CATCH(); + php_ion_unserialize_backref(obj, return_value); + ION_CATCH(); + return; + } + + if (has_annotation('R')) { + // REFERENCE + ZVAL_MAKE_REF(return_value); + ZVAL_DEREF(return_value); + next_annotation(); + } + + BOOL bval; + ION_CHECK(ion_reader_is_null(obj->reader, &bval)); + if (bval) { + goto read_null; + } + + switch (ION_TYPE_INT(typ)) { + case tid_NULL_INT: + read_null: ; + ION_CHECK(ion_reader_read_null(obj->reader, &typ)); + RETURN_NULL(); + + case tid_BOOL_INT: + ION_CHECK(ion_reader_read_bool(obj->reader, &bval)); + RETURN_BOOL(bval); + + case tid_INT_INT: + php_ion_unserialize_int(obj, return_value); + return; + + case tid_FLOAT_INT: + ION_CHECK(ion_reader_read_double(obj->reader, &Z_DVAL_P(return_value))); + return; + + case tid_DECIMAL_INT: + object_init_ex(return_value, ce_Decimal); + php_ion_decimal *dec = php_ion_obj(decimal, Z_OBJ_P(return_value)); + ION_CHECK(ion_reader_read_ion_decimal(obj->reader, &dec->dec)); + php_ion_decimal_ctor(dec); + zend_hash_next_index_insert(&php_ion_globals.unserializer.ids, return_value); + return; + + case tid_TIMESTAMP_INT: + php_ion_unserialize_timestamp(obj, return_value); + zend_hash_next_index_insert(&php_ion_globals.unserializer.ids, return_value); + return; + + case tid_SYMBOL_INT: + ION_SYMBOL sym; + ION_CHECK(ion_reader_read_ion_symbol(obj->reader, &sym)); + php_ion_symbol_zval(&sym, return_value); + zend_hash_next_index_insert(&php_ion_globals.unserializer.ids, return_value); + return; + + case tid_STRING_INT: + ION_STRING str; + ION_CHECK(ion_reader_read_string(obj->reader, &str)); + RETVAL_STRINGL((char *) str.value, str.length); + if (has_annotation('S')) { + goto serializable; + } + zend_hash_next_index_insert(&php_ion_globals.unserializer.ids, return_value); + return; + + case tid_CLOB_INT: + case tid_BLOB_INT: + php_ion_unserialize_lob(obj, return_value); + if (has_annotation('S')) { + goto serializable; + } + zend_hash_next_index_insert(&php_ion_globals.unserializer.ids, return_value); + return; + + case tid_LIST_INT: + case tid_SEXP_INT: // FIXME + php_ion_unserialize_list(obj, return_value); + return; + + case tid_STRUCT_INT: + serializable: ; + zend_string *class_name = NULL; + uint8_t object_type = annotation.length == 1 ? annotation.value[0] : 0; + if (object_type && object_type != 'o') { + next_annotation(); + class_name = zend_string_from_ion(&annotation); + } + php_ion_unserialize_struct(obj, object_type, class_name, return_value); + if (class_name) { + zend_string_release(class_name); + } + return; + + case tid_none_INT: + ZEND_ASSERT(0); + break; + + case tid_DATAGRAM_INT: + case tid_EOF_INT: + return; + } +} + +void php_ion_unserialize(php_ion_reader *obj, zend_string *zstr, zval *return_value) +{ + zval zreader; + + if (obj) { + ZVAL_OBJ_COPY(&zreader, &obj->std); + } else { + object_init_ex(&zreader, ce_Reader_Buffer_Reader); + obj = php_ion_obj(reader, Z_OBJ(zreader)); + obj->type = BUFFER_READER; + obj->buffer = zend_string_copy(zstr); + php_ion_reader_ctor(obj); + } + + php_ion_globals_serializer_step(); + php_ion_unserialize_zval(obj, return_value); + php_ion_globals_serializer_exit(); + + zval_ptr_dtor(&zreader); +} diff --git a/php_ion.h b/php_ion.h new file mode 100644 index 0000000..e1e158e --- /dev/null +++ b/php_ion.h @@ -0,0 +1,27 @@ +/* + +--------------------------------------------------------------------+ + | PECL :: ion | + +--------------------------------------------------------------------+ + | Redistribution and use in source and binary forms, with or without | + | modification, are permitted provided that the conditions mentioned | + | in the accompanying LICENSE file are met. | + +--------------------------------------------------------------------+ + | Copyright (c) 2021, Michael Wallner | + +--------------------------------------------------------------------+ +*/ + +#ifndef PHP_ION_H +# define PHP_ION_H + +extern zend_module_entry ion_module_entry; +# define phpext_ion_ptr &ion_module_entry + +# define PHP_ION_VERSION "0.1.0" + +# if defined(ZTS) && defined(COMPILE_DL_ION) +ZEND_TSRMLS_CACHE_EXTERN() +# endif + +#define SUCCESS 0 + +#endif /* PHP_ION_H */