improve symbol/table/catalog support
[awesomized/ext-ion] / ion.c
diff --git a/ion.c b/ion.c
index 017e88fda1d232ec5f5af1659b5de97a9472c6d9..5d10462038921384ae05cf1d43e31b49f3f20e4b 100644 (file)
--- a/ion.c
+++ b/ion.c
 #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)
+static ZEND_METHOD(ion_Symbol_ImportLocation, __construct)
 {
        php_ion_symbol_iloc *obj = php_ion_obj(symbol_iloc, Z_OBJ_P(ZEND_THIS));
        PTR_CHECK(obj);
@@ -50,7 +44,7 @@ ZEND_METHOD(ion_Symbol_ImportLocation, __construct)
        obj->loc.location = location;
        php_ion_symbol_iloc_ctor(obj);
 }
-ZEND_METHOD(ion_Symbol, __construct)
+static ZEND_METHOD(ion_Symbol, __construct)
 {
        php_ion_symbol *obj = php_ion_obj(symbol, Z_OBJ_P(ZEND_THIS));
        PTR_CHECK(obj);
@@ -66,7 +60,7 @@ ZEND_METHOD(ion_Symbol, __construct)
        obj->sym.sid = sid;
        php_ion_symbol_ctor(obj);
 }
-ZEND_METHOD(ion_Symbol, equals)
+static ZEND_METHOD(ion_Symbol, equals)
 {
        php_ion_symbol *sym = php_ion_obj(symbol, Z_OBJ_P(ZEND_THIS));
        OBJ_CHECK(sym);
@@ -83,7 +77,7 @@ ZEND_METHOD(ion_Symbol, equals)
        ION_CHECK(err);
        RETVAL_BOOL(eq);
 }
-ZEND_METHOD(ion_Symbol, __toString)
+static ZEND_METHOD(ion_Symbol, __toString)
 {
        php_ion_symbol *sym = php_ion_obj(symbol, Z_OBJ_P(ZEND_THIS));
        OBJ_CHECK(sym);
@@ -93,9 +87,317 @@ ZEND_METHOD(ion_Symbol, __toString)
        if (!sym->value) {
                RETURN_EMPTY_STRING();
        }
-       RETURN_STR(sym->value);
+       RETURN_STR_COPY(sym->value);
+}
+static ZEND_METHOD(ion_Symbol_Enum, toSymbol)
+{
+       ZEND_PARSE_PARAMETERS_NONE();
+
+       zval *zc = zend_enum_fetch_case_name(Z_OBJ_P(ZEND_THIS));
+       PTR_CHECK(zc);
+
+       zval *zsym = php_ion_global_symbol_fetch_by_enum(Z_STR_P(zc));
+       PTR_CHECK(zsym);
+       RETVAL_ZVAL(zsym, 1, 0);
+}
+static ZEND_METHOD(ion_Symbol_Enum, toSID)
+{
+       ZEND_PARSE_PARAMETERS_NONE();
+
+       zval *zc = zend_enum_fetch_case_name(Z_OBJ_P(ZEND_THIS));
+       PTR_CHECK(zc);
+
+       zval *zsym = php_ion_global_symbol_fetch_by_enum(Z_STR_P(zc));
+       PTR_CHECK(zsym);
+
+       zval tmp;
+       RETVAL_ZVAL(zend_read_property(Z_OBJCE_P(zsym), Z_OBJ_P(zsym), ZEND_STRL("sid"), 0, &tmp), 1, 0);
+}
+static ZEND_METHOD(ion_Symbol_Enum, toString)
+{
+       ZEND_PARSE_PARAMETERS_NONE();
+
+       zval *zc = zend_enum_fetch_case_name(Z_OBJ_P(ZEND_THIS));
+       PTR_CHECK(zc);
+
+       zval *zsym = php_ion_global_symbol_fetch_by_enum(Z_STR_P(zc));
+       PTR_CHECK(zsym);
+
+       zval tmp;
+       RETVAL_ZVAL(zend_read_property(Z_OBJCE_P(zsym), Z_OBJ_P(zsym), ZEND_STRL("value"), 0, &tmp), 1, 0);
+}
+static ZEND_FUNCTION(ion_Symbol_Table_System)
+{
+       ZEND_PARSE_PARAMETERS_NONE();
+
+       object_init_ex(return_value, ce_Symbol_Table_Shared);
+       php_ion_symbol_table *obj = php_ion_obj(symbol_table, Z_OBJ_P(return_value));
+       ION_CHECK(ion_symbol_table_get_system_table(&obj->tab, 1));
+       php_ion_symbol_table_ctor(obj);
+       ion_symbol_table_lock(obj->tab);
+}
+static ZEND_FUNCTION(ion_Symbol_Table_PHP)
+{
+       ZEND_PARSE_PARAMETERS_NONE();
+
+       object_init_ex(return_value, ce_Symbol_Table_Shared);
+       php_ion_symbol_table *obj = php_ion_obj(symbol_table, Z_OBJ_P(return_value));
+       obj->tab = g_sym_tab_php;
+       php_ion_symbol_table_ctor(obj);
+       ion_symbol_table_lock(obj->tab);
+}
+static ZEND_METHOD(ion_Symbol_Table_Local, __construct)
+{
+       php_ion_symbol_table *obj = php_ion_obj(symbol_table, Z_OBJ_P(ZEND_THIS));
+       PTR_CHECK(obj);
+
+       ZEND_PARSE_PARAMETERS_NONE();
+
+       ION_CHECK(ion_symbol_table_open_with_type(&obj->tab, NULL, ist_LOCAL));
+       obj->dtor = ion_symbol_table_close;
+       OBJ_CHECK(obj);
+}
+static ZEND_METHOD(ion_Symbol_Table_Local, import)
+{
+       php_ion_symbol_table *obj = php_ion_obj(symbol_table, Z_OBJ_P(ZEND_THIS));
+       OBJ_CHECK(obj);
+
+       zend_object *zo_import;
+       ZEND_PARSE_PARAMETERS_START(1, 1)
+               Z_PARAM_OBJ_OF_CLASS(zo_import, ce_Symbol_Table);
+       ZEND_PARSE_PARAMETERS_END();
+
+       php_ion_symbol_table_import(obj, php_ion_obj(symbol_table, zo_import));
+}
+static ZEND_METHOD(ion_Symbol_Table_Shared, __construct)
+{
+       php_ion_symbol_table *obj = php_ion_obj(symbol_table, Z_OBJ_P(ZEND_THIS));
+       PTR_CHECK(obj);
+
+       zend_string *zname;
+       zend_long version = 1;
+       ZEND_PARSE_PARAMETERS_START(1, 2)
+               Z_PARAM_STR(zname)
+               Z_PARAM_OPTIONAL
+               Z_PARAM_LONG(version)
+       ZEND_PARSE_PARAMETERS_END();
+
+       ION_CHECK(ion_symbol_table_open_with_type(&obj->tab, NULL, ist_SHARED));
+       obj->dtor = ion_symbol_table_close;
+
+       ION_STRING is;
+       ION_CHECK(ion_symbol_table_set_name(obj->tab, ion_string_from_zend(&is, zname)));
+       ION_CHECK(ion_symbol_table_set_version(obj->tab, version));
+
+       php_ion_symbol_table_ctor(obj);
+}
+static ZEND_METHOD(ion_Symbol_Table, getMaxId)
+{
+       php_ion_symbol_table *obj = php_ion_obj(symbol_table, Z_OBJ_P(ZEND_THIS));
+       OBJ_CHECK(obj);
+
+       ZEND_PARSE_PARAMETERS_NONE();
+
+       SID max_sid;
+       ION_CHECK(ion_symbol_table_get_max_sid(obj->tab, &max_sid));
+       RETURN_LONG(max_sid);
 }
-ZEND_METHOD(ion_Timestamp, __construct)
+static ZEND_METHOD(ion_Symbol_Table, add)
+{
+       php_ion_symbol_table *obj = php_ion_obj(symbol_table, Z_OBJ_P(ZEND_THIS));
+       OBJ_CHECK(obj);
+
+       zend_object *zo_sym = NULL;
+       zend_string *zs_sym = NULL;
+       ZEND_PARSE_PARAMETERS_START(1, 1)
+               Z_PARAM_OBJ_OF_CLASS_OR_STR(zo_sym, ce_Symbol, zs_sym)
+       ZEND_PARSE_PARAMETERS_END();
+
+       if (zo_sym) {
+               zval z_sym;
+               ZVAL_OBJ(&z_sym, zo_sym);
+               zs_sym = zval_get_string(&z_sym);
+               ION_CATCH();
+       }
+       SID sid;
+       ION_STRING is;
+       ION_CHECK(ion_symbol_table_add_symbol(obj->tab, ion_string_from_zend(&is, zs_sym), &sid),
+                       if (zo_sym) {
+                               zend_string_release(zs_sym);
+                       });
+       if (zo_sym) {
+               zend_string_release(zs_sym);
+       }
+       RETURN_LONG(sid);
+}
+static ZEND_METHOD(ion_Symbol_Table, find)
+{
+       php_ion_symbol_table *obj = php_ion_obj(symbol_table, Z_OBJ_P(ZEND_THIS));
+       OBJ_CHECK(obj);
+
+       zend_long zsid;
+       zend_string *zstring = NULL;
+       ZEND_PARSE_PARAMETERS_START(1, 1)
+               Z_PARAM_STR_OR_LONG(zstring, zsid)
+       ZEND_PARSE_PARAMETERS_END();
+
+       if (zstring) {
+               SID sid;
+               ION_STRING is;
+               ION_CHECK(ion_symbol_table_find_by_name(obj->tab, ion_string_from_zend(&is, zstring), &sid));
+               zsid = sid;
+       }
+       ION_SYMBOL *sym;
+       ION_CHECK(ion_symbol_table_get_symbol(obj->tab, zsid, &sym));
+       if (sym) {
+               php_ion_symbol_table_symbol_zval(obj, sym, return_value);
+       }
+}
+static ZEND_METHOD(ion_Symbol_Table, findLocal)
+{
+       php_ion_symbol_table *obj = php_ion_obj(symbol_table, Z_OBJ_P(ZEND_THIS));
+       OBJ_CHECK(obj);
+
+       zend_long zsid;
+       zend_string *zstring;
+       ZEND_PARSE_PARAMETERS_START(1, 1)
+               Z_PARAM_STR_OR_LONG(zstring, zsid)
+       ZEND_PARSE_PARAMETERS_END();
+
+       if (zstring) {
+               SID sid;
+               ION_STRING is;
+               ION_CHECK(ion_symbol_table_find_by_name(obj->tab, ion_string_from_zend(&is, zstring), &sid));
+               zsid = sid;
+       }
+       ION_SYMBOL *sym;
+       ION_CHECK(ion_symbol_table_get_local_symbol(obj->tab, zsid, &sym));
+       if (sym) {
+               php_ion_symbol_table_symbol_zval(obj, sym, return_value);
+       }
+}
+static ZEND_METHOD(ion_Catalog, __construct)
+{
+       php_ion_catalog *obj = php_ion_obj(catalog, Z_OBJ_P(ZEND_THIS));
+       PTR_CHECK(obj);
+
+       ZEND_PARSE_PARAMETERS_NONE();
+
+       php_ion_catalog_ctor(obj);
+}
+static ZEND_METHOD(ion_Catalog, count)
+{
+       php_ion_catalog *obj = php_ion_obj(catalog, Z_OBJ_P(ZEND_THIS));
+       OBJ_CHECK(obj);
+
+       ZEND_PARSE_PARAMETERS_NONE();
+
+       int32_t c;
+       ION_CHECK(ion_catalog_get_symbol_table_count(obj->cat, &c));
+       RETURN_LONG(c);
+}
+static ZEND_METHOD(ion_Catalog, add)
+{
+       php_ion_catalog *obj = php_ion_obj(catalog, Z_OBJ_P(ZEND_THIS));
+       OBJ_CHECK(obj);
+
+       zend_object *zo_symtab;
+       ZEND_PARSE_PARAMETERS_START(1, 1)
+               Z_PARAM_OBJ_OF_CLASS(zo_symtab, ce_Symbol_Table)
+       ZEND_PARSE_PARAMETERS_END();
+
+       php_ion_symbol_table *o_symtab = php_ion_obj(symbol_table, zo_symtab);
+       php_ion_catalog_add_symbol_table(obj, o_symtab);
+}
+struct remove_symtab_ctx {
+       const char *name;
+       zend_bool deleted;
+};
+static int remove_symtab(zval *ztab, void *ctx)
+{
+       struct remove_symtab_ctx *rsc = ctx;
+       php_ion_symbol_table *tab = php_ion_obj(symbol_table, Z_OBJ_P(ztab));
+       if (tab && tab->tab) {
+               ION_STRING is;
+               if (IERR_OK == ion_symbol_table_get_name(tab->tab, &is)) {
+                       if (strcmp((const char *) is.value, rsc->name)) {
+                               return ZEND_HASH_APPLY_KEEP;
+                       }
+               }
+       }
+       rsc->deleted = true;
+       return ZEND_HASH_APPLY_REMOVE;
+
+}
+static ZEND_METHOD(ion_Catalog, remove)
+{
+       php_ion_catalog *obj = php_ion_obj(catalog, Z_OBJ_P(ZEND_THIS));
+       OBJ_CHECK(obj);
+
+       zend_object *zo_symtab = NULL;
+       zend_string *zs_symtab = NULL;
+       ZEND_PARSE_PARAMETERS_START(1, 1)
+               Z_PARAM_OBJ_OF_CLASS_OR_STR(zo_symtab, ce_Symbol_Table, zs_symtab)
+       ZEND_PARSE_PARAMETERS_END();
+
+       RETVAL_FALSE;
+
+       zval tmp;
+       zval *ztabs = zend_read_property(obj->std.ce, &obj->std, ZEND_STRL("symbolTables"), 0, &tmp);
+       if (ztabs) {
+               if (zo_symtab) {
+                       // fast path
+                       zend_ulong idx = (uintptr_t) &zo_symtab->gc;
+                       RETURN_BOOL(SUCCESS == zend_hash_index_del(Z_ARRVAL_P(ztabs), idx));
+               } else {
+                       // iterate over all symbol tables and delete any with matching name
+                       struct remove_symtab_ctx ctx = {zs_symtab->val, false};
+                       zend_hash_apply_with_argument(Z_ARRVAL_P(ztabs), remove_symtab, &ctx);
+                       RETURN_BOOL(ctx.deleted);
+               }
+       }
+}
+static ZEND_METHOD(ion_Catalog, find)
+{
+       php_ion_catalog *obj = php_ion_obj(catalog, Z_OBJ_P(ZEND_THIS));
+       OBJ_CHECK(obj);
+
+       zend_long zversion = 0;
+       zend_string *zname;
+       ZEND_PARSE_PARAMETERS_START(1, 2)
+               Z_PARAM_STR(zname)
+               Z_PARAM_OPTIONAL
+               Z_PARAM_LONG(zversion)
+       ZEND_PARSE_PARAMETERS_END();
+
+       ION_STRING is;
+       ION_SYMBOL_TABLE *tab_ptr = NULL;
+       ION_CHECK(ion_catalog_find_symbol_table(obj->cat, ion_string_from_zend(&is, zname), zversion, &tab_ptr));
+       if (tab_ptr) {
+               php_ion_catalog_symbol_table_zval(obj, tab_ptr, return_value);
+       }
+}
+static ZEND_METHOD(ion_Catalog, findBest)
+{
+       php_ion_catalog *obj = php_ion_obj(catalog, Z_OBJ_P(ZEND_THIS));
+       OBJ_CHECK(obj);
+
+       zend_long zversion = 0;
+       zend_string *zname;
+       ZEND_PARSE_PARAMETERS_START(1, 2)
+               Z_PARAM_STR(zname)
+               Z_PARAM_OPTIONAL
+               Z_PARAM_LONG(zversion)
+       ZEND_PARSE_PARAMETERS_END();
+
+       ION_STRING is;
+       ION_SYMBOL_TABLE *tab_ptr = NULL;
+       ION_CHECK(ion_catalog_find_best_match(obj->cat, ion_string_from_zend(&is, zname), zversion, &tab_ptr));
+       if (tab_ptr) {
+               php_ion_catalog_symbol_table_zval(obj, tab_ptr, return_value);
+       }
+}
+static ZEND_METHOD(ion_Timestamp, __construct)
 {
        php_ion_timestamp *obj = php_ion_obj(timestamp, Z_OBJ_P(ZEND_THIS));
        PTR_CHECK(obj);
@@ -117,7 +419,7 @@ ZEND_METHOD(ion_Timestamp, __construct)
        }
        php_ion_timestamp_ctor(obj, precision, fmt, dt, tz);
 }
-ZEND_METHOD(ion_Timestamp, __toString)
+static ZEND_METHOD(ion_Timestamp, __toString)
 {
        php_ion_timestamp *obj = php_ion_obj(timestamp, Z_OBJ_P(ZEND_THIS));
        OBJ_CHECK(obj);
@@ -129,21 +431,67 @@ ZEND_METHOD(ion_Timestamp, __toString)
        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_Context, __construct)
+static ZEND_METHOD(ion_Decimal_Context, __construct)
 {
        php_ion_decimal_ctx *obj = php_ion_obj(decimal_ctx, Z_OBJ_P(ZEND_THIS));
        PTR_CHECK(obj);
 
-       zend_long bits = 128;
+       zend_bool clamp;
+       zend_object *o_round = NULL;
+       zend_long digits, emax, emin, round;
+       ZEND_PARSE_PARAMETERS_START(5, 5)
+               Z_PARAM_LONG(digits)
+               Z_PARAM_LONG(emax)
+               Z_PARAM_LONG(emin)
+               Z_PARAM_OBJ_OF_CLASS_OR_LONG(o_round, ce_Decimal_Context_Rounding, round)
+               Z_PARAM_BOOL(clamp)
+       ZEND_PARSE_PARAMETERS_END();
+
+       if (o_round) {
+               round = Z_LVAL_P(zend_enum_fetch_case_value(o_round));
+       }
+       php_ion_decimal_ctx_init(&obj->ctx, digits, emax, emin, round, clamp);
+       php_ion_decimal_ctx_ctor(obj, o_round);
+}
+static inline void make_decimal_ctx(INTERNAL_FUNCTION_PARAMETERS, int kind)
+{
+       ZEND_PARSE_PARAMETERS_NONE();
+
+       object_init_ex(return_value, ce_Decimal_Context);
+       php_ion_decimal_ctx *obj = php_ion_obj(decimal_ctx, Z_OBJ_P(return_value));
+       decContextDefault(&obj->ctx, kind);
+       php_ion_decimal_ctx_ctor(obj, NULL);
+}
+static ZEND_METHOD(ion_Decimal_Context, Dec32)
+{
+       make_decimal_ctx(INTERNAL_FUNCTION_PARAM_PASSTHRU, DEC_INIT_DECIMAL32);
+}
+static ZEND_METHOD(ion_Decimal_Context, Dec64)
+{
+       make_decimal_ctx(INTERNAL_FUNCTION_PARAM_PASSTHRU, DEC_INIT_DECIMAL64);
+}
+static ZEND_METHOD(ion_Decimal_Context, Dec128)
+{
+       make_decimal_ctx(INTERNAL_FUNCTION_PARAM_PASSTHRU, DEC_INIT_DECIMAL128);
+}
+static ZEND_METHOD(ion_Decimal_Context, DecMax)
+{
+       zend_object *o_round = NULL;
+       zend_long round = DEC_ROUND_HALF_EVEN;
        ZEND_PARSE_PARAMETERS_START(0, 1)
                Z_PARAM_OPTIONAL
-               Z_PARAM_LONG(bits)
+               Z_PARAM_OBJ_OF_CLASS_OR_LONG(o_round, ce_Decimal_Context_Rounding, round)
        ZEND_PARSE_PARAMETERS_END();
 
-       zend_update_property_long(obj->std.ce, &obj->std, ZEND_STRL("bits"), bits);
-       php_ion_decimal_ctx_ctor(obj);
+       if (o_round) {
+               round = Z_LVAL_P(zend_enum_fetch_case_value(o_round));
+       }
+       object_init_ex(return_value, ce_Decimal_Context);
+       php_ion_decimal_ctx *obj = php_ion_obj(decimal_ctx, Z_OBJ_P(return_value));
+       php_ion_decimal_ctx_init_max(&obj->ctx, round);
+       php_ion_decimal_ctx_ctor(obj, o_round);
 }
-ZEND_METHOD(ion_Decimal, __construct)
+static ZEND_METHOD(ion_Decimal, __construct)
 {
        php_ion_decimal *obj = php_ion_obj(decimal, Z_OBJ_P(ZEND_THIS));
        PTR_CHECK(obj);
@@ -162,26 +510,21 @@ ZEND_METHOD(ion_Decimal, __construct)
                zval zdc;
                object_init_ex(&zdc, ce_Decimal_Context);
                obj->ctx = Z_OBJ(zdc);
-               php_ion_decimal_ctx_ctor(php_ion_obj(decimal_ctx, obj->ctx));
+               php_ion_decimal_ctx_ctor(php_ion_obj(decimal_ctx, obj->ctx), NULL);
        }
 
+       decContext *ctx = &php_ion_obj(decimal_ctx, obj->ctx)->ctx;
+
        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));
+               ION_CHECK(ion_decimal_from_string(&obj->dec, zstr->val, ctx), OBJ_RELEASE(obj->ctx));
        } 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_from_zend_long(&obj->dec, ctx, num);
        }
 
        php_ion_decimal_ctor(obj);
        OBJ_RELEASE(obj->ctx);
 }
-ZEND_METHOD(ion_Decimal, equals)
+static ZEND_METHOD(ion_Decimal, equals)
 {
        php_ion_decimal *obj = php_ion_obj(decimal, Z_OBJ_P(ZEND_THIS));
        PTR_CHECK(obj);
@@ -196,7 +539,7 @@ ZEND_METHOD(ion_Decimal, equals)
                obj->ctx ? &php_ion_obj(decimal_ctx, obj->ctx)->ctx : NULL, &is));
        RETURN_BOOL(is);
 }
-ZEND_METHOD(ion_Decimal, __toString)
+static ZEND_METHOD(ion_Decimal, __toString)
 {
        php_ion_decimal *obj = php_ion_obj(decimal, Z_OBJ_P(ZEND_THIS));
        PTR_CHECK(obj);
@@ -205,7 +548,7 @@ ZEND_METHOD(ion_Decimal, __toString)
 
        RETURN_STR(php_ion_decimal_to_string(&obj->dec));
 }
-ZEND_METHOD(ion_Decimal, toInt)
+static ZEND_METHOD(ion_Decimal, toInt)
 {
        php_ion_decimal *obj = php_ion_obj(decimal, Z_OBJ_P(ZEND_THIS));
        PTR_CHECK(obj);
@@ -213,10 +556,10 @@ ZEND_METHOD(ion_Decimal, toInt)
        ZEND_PARSE_PARAMETERS_NONE();
 
        zend_long l;
-       php_ion_decimal_to_int(&obj->dec, &php_ion_obj(decimal_ctx, obj->ctx)->ctx, &l);
+       php_ion_decimal_to_zend_long(&obj->dec, &php_ion_obj(decimal_ctx, obj->ctx)->ctx, &l);
        RETURN_LONG(l);
 }
-ZEND_METHOD(ion_Decimal, isInt)
+static ZEND_METHOD(ion_Decimal, isInt)
 {
        php_ion_decimal *obj = php_ion_obj(decimal, Z_OBJ_P(ZEND_THIS));
        PTR_CHECK(obj);
@@ -225,7 +568,23 @@ ZEND_METHOD(ion_Decimal, isInt)
 
        RETURN_BOOL(ion_decimal_is_integer(&obj->dec));
 }
-ZEND_METHOD(ion_Reader_Options, __construct)
+static ZEND_METHOD(ion_LOB, __construct)
+{
+       zend_string *value;
+       zend_object *type = NULL;
+       ZEND_PARSE_PARAMETERS_START(1, 2)
+               Z_PARAM_STR(value)
+               Z_PARAM_OPTIONAL
+               Z_PARAM_OBJ_OF_CLASS(type, ce_Type)
+       ZEND_PARSE_PARAMETERS_END();
+
+       if (!type) {
+               type = zend_enum_get_case_cstr(ce_Type, "CLob");
+       }
+       update_property_obj(Z_OBJ_P(ZEND_THIS), ZEND_STRL("type"), type);
+       zend_update_property_str(Z_OBJCE_P(ZEND_THIS), Z_OBJ_P(ZEND_THIS), ZEND_STRL("value"), value);
+}
+static 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;
@@ -297,7 +656,7 @@ ZEND_METHOD(ion_Reader_Options, __construct)
        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)
+static ZEND_METHOD(ion_Reader_Reader, hasChildren)
 {
        php_ion_reader *obj = php_ion_obj(reader, Z_OBJ_P(ZEND_THIS));
 
@@ -317,7 +676,7 @@ ZEND_METHOD(ion_Reader_Reader, hasChildren)
                        break;
        }
 }
-ZEND_METHOD(ion_Reader_Reader, getChildren)
+static ZEND_METHOD(ion_Reader_Reader, getChildren)
 {
        php_ion_reader *obj = php_ion_obj(reader, Z_OBJ_P(ZEND_THIS));
        OBJ_CHECK(obj);
@@ -328,7 +687,7 @@ ZEND_METHOD(ion_Reader_Reader, getChildren)
 
        RETURN_ZVAL(ZEND_THIS, 1, 0);
 }
-ZEND_METHOD(ion_Reader_Reader, rewind)
+static ZEND_METHOD(ion_Reader_Reader, rewind)
 {
        php_ion_reader *obj = php_ion_obj(reader, Z_OBJ_P(ZEND_THIS));
        OBJ_CHECK(obj);
@@ -337,7 +696,7 @@ ZEND_METHOD(ion_Reader_Reader, rewind)
 
        ION_CHECK(ion_reader_next(obj->reader, &obj->state));
 }
-ZEND_METHOD(ion_Reader_Reader, next)
+static ZEND_METHOD(ion_Reader_Reader, next)
 {
        php_ion_reader *obj = php_ion_obj(reader, Z_OBJ_P(ZEND_THIS));
        OBJ_CHECK(obj);
@@ -353,7 +712,7 @@ ZEND_METHOD(ion_Reader_Reader, next)
        }
        ION_CHECK(ion_reader_next(obj->reader, &obj->state));
 }
-ZEND_METHOD(ion_Reader_Reader, valid)
+static ZEND_METHOD(ion_Reader_Reader, valid)
 {
        php_ion_reader *obj = php_ion_obj(reader, Z_OBJ_P(ZEND_THIS));
        OBJ_CHECK(obj);
@@ -361,7 +720,7 @@ ZEND_METHOD(ion_Reader_Reader, valid)
        ZEND_PARSE_PARAMETERS_NONE();
        RETURN_BOOL(obj->state != tid_none && obj->state != tid_EOF);
 }
-ZEND_METHOD(ion_Reader_Reader, key)
+static ZEND_METHOD(ion_Reader_Reader, key)
 {
        php_ion_reader *obj = php_ion_obj(reader, Z_OBJ_P(ZEND_THIS));
        OBJ_CHECK(obj);
@@ -369,12 +728,12 @@ ZEND_METHOD(ion_Reader_Reader, key)
        ZEND_PARSE_PARAMETERS_NONE();
        RETURN_IONTYPE(obj->state);
 }
-ZEND_METHOD(ion_Reader_Reader, current)
+static ZEND_METHOD(ion_Reader_Reader, current)
 {
        ZEND_PARSE_PARAMETERS_NONE();
        RETURN_ZVAL(ZEND_THIS, 1, 0);
 }
-ZEND_METHOD(ion_Reader_Reader, getType)
+static ZEND_METHOD(ion_Reader_Reader, getType)
 {
        php_ion_reader *obj = php_ion_obj(reader, Z_OBJ_P(ZEND_THIS));
        OBJ_CHECK(obj);
@@ -385,7 +744,7 @@ ZEND_METHOD(ion_Reader_Reader, getType)
        ION_CHECK(ion_reader_get_type(obj->reader, &typ));
        RETURN_IONTYPE(typ);
 }
-ZEND_METHOD(ion_Reader_Reader, hasAnnotations)
+static ZEND_METHOD(ion_Reader_Reader, hasAnnotations)
 {
        php_ion_reader *obj = php_ion_obj(reader, Z_OBJ_P(ZEND_THIS));
        OBJ_CHECK(obj);
@@ -396,7 +755,7 @@ ZEND_METHOD(ion_Reader_Reader, hasAnnotations)
        ION_CHECK(ion_reader_has_any_annotations(obj->reader, &has));
        RETURN_BOOL(has);
 }
-ZEND_METHOD(ion_Reader_Reader, hasAnnotation)
+static ZEND_METHOD(ion_Reader_Reader, hasAnnotation)
 {
        php_ion_reader *obj = php_ion_obj(reader, Z_OBJ_P(ZEND_THIS));
        OBJ_CHECK(obj);
@@ -411,7 +770,7 @@ ZEND_METHOD(ion_Reader_Reader, hasAnnotation)
        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)
+static ZEND_METHOD(ion_Reader_Reader, isNull)
 {
        php_ion_reader *obj = php_ion_obj(reader, Z_OBJ_P(ZEND_THIS));
        OBJ_CHECK(obj);
@@ -422,7 +781,7 @@ ZEND_METHOD(ion_Reader_Reader, isNull)
        ION_CHECK(ion_reader_is_null(obj->reader, &is));
        RETURN_BOOL(is);
 }
-ZEND_METHOD(ion_Reader_Reader, isInStruct)
+static ZEND_METHOD(ion_Reader_Reader, isInStruct)
 {
        php_ion_reader *obj = php_ion_obj(reader, Z_OBJ_P(ZEND_THIS));
        OBJ_CHECK(obj);
@@ -433,7 +792,7 @@ ZEND_METHOD(ion_Reader_Reader, isInStruct)
        ION_CHECK(ion_reader_is_in_struct(obj->reader, &is));
        RETURN_BOOL(is);
 }
-ZEND_METHOD(ion_Reader_Reader, getFieldName)
+static ZEND_METHOD(ion_Reader_Reader, getFieldName)
 {
        php_ion_reader *obj = php_ion_obj(reader, Z_OBJ_P(ZEND_THIS));
        OBJ_CHECK(obj);
@@ -444,7 +803,7 @@ ZEND_METHOD(ion_Reader_Reader, getFieldName)
        ION_CHECK(ion_reader_get_field_name(obj->reader, &name));
        RETURN_STRINGL((char *) name.value, name.length);
 }
-ZEND_METHOD(ion_Reader_Reader, getFieldNameSymbol)
+static ZEND_METHOD(ion_Reader_Reader, getFieldNameSymbol)
 {
        php_ion_reader *obj = php_ion_obj(reader, Z_OBJ_P(ZEND_THIS));
        OBJ_CHECK(obj);
@@ -456,7 +815,7 @@ ZEND_METHOD(ion_Reader_Reader, getFieldNameSymbol)
 
        php_ion_symbol_zval(sym_ptr, return_value);
 }
-ZEND_METHOD(ion_Reader_Reader, getAnnotations)
+static ZEND_METHOD(ion_Reader_Reader, getAnnotations)
 {
        php_ion_reader *obj = php_ion_obj(reader, Z_OBJ_P(ZEND_THIS));
        OBJ_CHECK(obj);
@@ -480,7 +839,7 @@ ZEND_METHOD(ion_Reader_Reader, getAnnotations)
        efree(ptr);
        ION_CHECK(err);
 }
-ZEND_METHOD(ion_Reader_Reader, getAnnotationSymbols)
+static ZEND_METHOD(ion_Reader_Reader, getAnnotationSymbols)
 {
        php_ion_reader *obj = php_ion_obj(reader, Z_OBJ_P(ZEND_THIS));
        OBJ_CHECK(obj);
@@ -501,7 +860,7 @@ ZEND_METHOD(ion_Reader_Reader, getAnnotationSymbols)
        efree(ptr);
        ION_CHECK(err);
 }
-ZEND_METHOD(ion_Reader_Reader, countAnnotations)
+static ZEND_METHOD(ion_Reader_Reader, countAnnotations)
 {
        php_ion_reader *obj = php_ion_obj(reader, Z_OBJ_P(ZEND_THIS));
        OBJ_CHECK(obj);
@@ -512,7 +871,7 @@ ZEND_METHOD(ion_Reader_Reader, countAnnotations)
        ION_CHECK(ion_reader_get_annotation_count(obj->reader, &sz));
        RETURN_LONG(sz);
 }
-ZEND_METHOD(ion_Reader_Reader, getAnnotation)
+static ZEND_METHOD(ion_Reader_Reader, getAnnotation)
 {
        php_ion_reader *obj = php_ion_obj(reader, Z_OBJ_P(ZEND_THIS));
        OBJ_CHECK(obj);
@@ -526,7 +885,7 @@ ZEND_METHOD(ion_Reader_Reader, getAnnotation)
        ION_CHECK(ion_reader_get_an_annotation(obj->reader, idx, &ann));
        RETURN_STRINGL((char *) ann.value, ann.length);
 }
-ZEND_METHOD(ion_Reader_Reader, getAnnotationSymbol)
+static ZEND_METHOD(ion_Reader_Reader, getAnnotationSymbol)
 {
        php_ion_reader *obj = php_ion_obj(reader, Z_OBJ_P(ZEND_THIS));
        OBJ_CHECK(obj);
@@ -540,7 +899,7 @@ ZEND_METHOD(ion_Reader_Reader, getAnnotationSymbol)
        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)
+static ZEND_METHOD(ion_Reader_Reader, readNull)
 {
        php_ion_reader *obj = php_ion_obj(reader, Z_OBJ_P(ZEND_THIS));
        OBJ_CHECK(obj);
@@ -551,7 +910,7 @@ ZEND_METHOD(ion_Reader_Reader, readNull)
        ION_CHECK(ion_reader_read_null(obj->reader, &typ));
        RETURN_OBJ_COPY(php_ion_type_fetch(typ));
 }
-ZEND_METHOD(ion_Reader_Reader, readBool)
+static ZEND_METHOD(ion_Reader_Reader, readBool)
 {
        php_ion_reader *obj = php_ion_obj(reader, Z_OBJ_P(ZEND_THIS));
        OBJ_CHECK(obj);
@@ -562,16 +921,16 @@ ZEND_METHOD(ion_Reader_Reader, readBool)
        ION_CHECK(ion_reader_read_bool(obj->reader, &b));
        RETURN_BOOL(b);
 }
-ZEND_METHOD(ion_Reader_Reader, readInt)
+static 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);
+       php_ion_reader_read_int(obj->reader, return_value);
 }
-ZEND_METHOD(ion_Reader_Reader, readFloat)
+static ZEND_METHOD(ion_Reader_Reader, readFloat)
 {
        php_ion_reader *obj = php_ion_obj(reader, Z_OBJ_P(ZEND_THIS));
        OBJ_CHECK(obj);
@@ -580,7 +939,7 @@ ZEND_METHOD(ion_Reader_Reader, readFloat)
 
        ION_CHECK(ion_reader_read_double(obj->reader, &Z_DVAL_P(return_value)));
 }
-ZEND_METHOD(ion_Reader_Reader, readDecimal)
+static ZEND_METHOD(ion_Reader_Reader, readDecimal)
 {
        php_ion_reader *obj = php_ion_obj(reader, Z_OBJ_P(ZEND_THIS));
        OBJ_CHECK(obj);
@@ -592,16 +951,16 @@ ZEND_METHOD(ion_Reader_Reader, readDecimal)
        ION_CHECK(ion_reader_read_ion_decimal(obj->reader, &dec->dec));
        php_ion_decimal_ctor(dec);
 }
-ZEND_METHOD(ion_Reader_Reader, readTimestamp)
+static 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);
+       php_ion_reader_read_timestamp(obj->reader, obj->opt ? &php_ion_obj(reader_options, obj->opt)->opt : NULL, return_value);
 }
-ZEND_METHOD(ion_Reader_Reader, readSymbol)
+static ZEND_METHOD(ion_Reader_Reader, readSymbol)
 {
        php_ion_reader *obj = php_ion_obj(reader, Z_OBJ_P(ZEND_THIS));
        OBJ_CHECK(obj);
@@ -612,7 +971,7 @@ ZEND_METHOD(ion_Reader_Reader, readSymbol)
        ION_CHECK(ion_reader_read_ion_symbol(obj->reader, &sym));
        php_ion_symbol_zval(&sym, return_value);
 }
-ZEND_METHOD(ion_Reader_Reader, readString)
+static ZEND_METHOD(ion_Reader_Reader, readString)
 {
        php_ion_reader *obj = php_ion_obj(reader, Z_OBJ_P(ZEND_THIS));
        OBJ_CHECK(obj);
@@ -665,24 +1024,24 @@ fail:
        ZVAL_EMPTY_STRING(ref);
        RETURN_FALSE;
 }
-ZEND_METHOD(ion_Reader_Reader, readStringPart)
+static ZEND_METHOD(ion_Reader_Reader, readStringPart)
 {
        read_part(INTERNAL_FUNCTION_PARAM_PASSTHRU, ion_reader_read_partial_string);
 }
-ZEND_METHOD(ion_Reader_Reader, readLob)
+static 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);
+       php_ion_reader_read_lob(obj->reader, return_value);
 }
-ZEND_METHOD(ion_Reader_Reader, readLobPart)
+static ZEND_METHOD(ion_Reader_Reader, readLobPart)
 {
        read_part(INTERNAL_FUNCTION_PARAM_PASSTHRU, ion_reader_read_lob_partial_bytes);
 }
-ZEND_METHOD(ion_Reader_Reader, getPosition)
+static ZEND_METHOD(ion_Reader_Reader, getPosition)
 {
        php_ion_reader *obj = php_ion_obj(reader, Z_OBJ_P(ZEND_THIS));
        OBJ_CHECK(obj);
@@ -694,7 +1053,7 @@ ZEND_METHOD(ion_Reader_Reader, getPosition)
        ION_CHECK(ion_reader_get_position(obj->reader, &bytes, &dummy, &dummy));
        RETURN_LONG(bytes);
 }
-ZEND_METHOD(ion_Reader_Reader, getDepth)
+static ZEND_METHOD(ion_Reader_Reader, getDepth)
 {
        php_ion_reader *obj = php_ion_obj(reader, Z_OBJ_P(ZEND_THIS));
        OBJ_CHECK(obj);
@@ -705,7 +1064,7 @@ ZEND_METHOD(ion_Reader_Reader, getDepth)
        ION_CHECK(ion_reader_get_depth(obj->reader, &depth));
        RETURN_LONG(depth);
 }
-ZEND_METHOD(ion_Reader_Reader, seek)
+static ZEND_METHOD(ion_Reader_Reader, seek)
 {
        php_ion_reader *obj = php_ion_obj(reader, Z_OBJ_P(ZEND_THIS));
        OBJ_CHECK(obj);
@@ -719,7 +1078,7 @@ ZEND_METHOD(ion_Reader_Reader, seek)
 
        ION_CHECK(ion_reader_seek(obj->reader, off, len));
 }
-ZEND_METHOD(ion_Reader_Reader, getValueOffset)
+static ZEND_METHOD(ion_Reader_Reader, getValueOffset)
 {
        php_ion_reader *obj = php_ion_obj(reader, Z_OBJ_P(ZEND_THIS));
        OBJ_CHECK(obj);
@@ -730,7 +1089,7 @@ ZEND_METHOD(ion_Reader_Reader, getValueOffset)
        ION_CHECK(ion_reader_get_value_offset(obj->reader, &off));
        RETURN_LONG(off);
 }
-ZEND_METHOD(ion_Reader_Reader, getValueLength)
+static ZEND_METHOD(ion_Reader_Reader, getValueLength)
 {
        php_ion_reader *obj = php_ion_obj(reader, Z_OBJ_P(ZEND_THIS));
        OBJ_CHECK(obj);
@@ -741,7 +1100,7 @@ ZEND_METHOD(ion_Reader_Reader, getValueLength)
        ION_CHECK(ion_reader_get_value_length(obj->reader, &len));
        RETURN_LONG(len);
 }
-ZEND_METHOD(ion_Reader_Buffer_Reader, __construct)
+static ZEND_METHOD(ion_Reader_Buffer_Reader, __construct)
 {
        php_ion_reader *obj = php_ion_obj(reader, Z_OBJ_P(ZEND_THIS));
        PTR_CHECK(obj);
@@ -758,7 +1117,7 @@ ZEND_METHOD(ion_Reader_Buffer_Reader, __construct)
 
        php_ion_reader_ctor(obj);
 }
-ZEND_METHOD(ion_Reader_Buffer_Reader, getBuffer)
+static ZEND_METHOD(ion_Reader_Buffer_Reader, getBuffer)
 {
        php_ion_reader *obj = php_ion_obj(reader, Z_OBJ_P(ZEND_THIS));
        OBJ_CHECK(obj);
@@ -767,7 +1126,7 @@ ZEND_METHOD(ion_Reader_Buffer_Reader, getBuffer)
        RETURN_STR_COPY(obj->buffer);
 }
 
-ZEND_METHOD(ion_Reader_Stream_Reader, __construct)
+static ZEND_METHOD(ion_Reader_Stream_Reader, __construct)
 {
        php_ion_reader *obj = php_ion_obj(reader, Z_OBJ_P(ZEND_THIS));
        PTR_CHECK(obj);
@@ -784,7 +1143,7 @@ ZEND_METHOD(ion_Reader_Stream_Reader, __construct)
 
        php_ion_reader_ctor(obj);
 }
-ZEND_METHOD(ion_Reader_Stream_Reader, getStream)
+static ZEND_METHOD(ion_Reader_Stream_Reader, getStream)
 {
        php_ion_reader *obj = php_ion_obj(reader, Z_OBJ_P(ZEND_THIS));
        OBJ_CHECK(obj);
@@ -795,7 +1154,7 @@ ZEND_METHOD(ion_Reader_Stream_Reader, getStream)
        GC_ADDREF(obj->stream.ptr->res);
        RETURN_RES(obj->stream.ptr->res);
 }
-ZEND_METHOD(ion_Reader_Stream_Reader, resetStream)
+static ZEND_METHOD(ion_Reader_Stream_Reader, resetStream)
 {
        php_ion_reader *obj = php_ion_obj(reader, Z_OBJ_P(ZEND_THIS));
        OBJ_CHECK(obj);
@@ -814,7 +1173,7 @@ ZEND_METHOD(ion_Reader_Stream_Reader, resetStream)
        PTR_CHECK(obj->stream.ptr);
        Z_ADDREF_P(zstream);
 }
-ZEND_METHOD(ion_Reader_Stream_Reader, resetStreamWithLength)
+static ZEND_METHOD(ion_Reader_Stream_Reader, resetStreamWithLength)
 {
        php_ion_reader *obj = php_ion_obj(reader, Z_OBJ_P(ZEND_THIS));
        OBJ_CHECK(obj);
@@ -835,7 +1194,7 @@ ZEND_METHOD(ion_Reader_Stream_Reader, resetStreamWithLength)
        PTR_CHECK(obj->stream.ptr);
        Z_ADDREF_P(zstream);
 }
-ZEND_METHOD(ion_Writer_Options, __construct)
+static ZEND_METHOD(ion_Writer_Options, __construct)
 {
        php_ion_writer_options *obj = php_ion_obj(writer_options, Z_OBJ_P(ZEND_THIS));
        PTR_CHECK(obj);
@@ -917,7 +1276,7 @@ ZEND_METHOD(ion_Writer_Options, __construct)
        zend_update_property_long(obj->std.ce, &obj->std, ZEND_STRL("allocationPageSize"),
                        obj->opt.allocation_page_size = alloc);
 }
-ZEND_METHOD(ion_Writer_Writer, writeNull)
+static ZEND_METHOD(ion_Writer_Writer, writeNull)
 {
        php_ion_writer *obj = php_ion_obj(writer, Z_OBJ_P(ZEND_THIS));
        OBJ_CHECK(obj);
@@ -926,7 +1285,7 @@ ZEND_METHOD(ion_Writer_Writer, writeNull)
 
        ION_CHECK(ion_writer_write_null(obj->writer));
 }
-ZEND_METHOD(ion_Writer_Writer, writeTypedNull)
+static ZEND_METHOD(ion_Writer_Writer, writeTypedNull)
 {
        php_ion_writer *obj = php_ion_obj(writer, Z_OBJ_P(ZEND_THIS));
        OBJ_CHECK(obj);
@@ -940,7 +1299,7 @@ ZEND_METHOD(ion_Writer_Writer, writeTypedNull)
        OBJ_CHECK(typ);
        ION_CHECK(ion_writer_write_typed_null(obj->writer, php_ion_obj(type, typ)->typ));
 }
-ZEND_METHOD(ion_Writer_Writer, writeBool)
+static ZEND_METHOD(ion_Writer_Writer, writeBool)
 {
        php_ion_writer *obj = php_ion_obj(writer, Z_OBJ_P(ZEND_THIS));
        OBJ_CHECK(obj);
@@ -952,7 +1311,7 @@ ZEND_METHOD(ion_Writer_Writer, writeBool)
 
        ION_CHECK(ion_writer_write_bool(obj->writer, b));
 }
-ZEND_METHOD(ion_Writer_Writer, writeInt)
+static ZEND_METHOD(ion_Writer_Writer, writeInt)
 {
        php_ion_writer *obj = php_ion_obj(writer, Z_OBJ_P(ZEND_THIS));
        OBJ_CHECK(obj);
@@ -973,7 +1332,7 @@ ZEND_METHOD(ion_Writer_Writer, writeInt)
                ION_CHECK(ion_writer_write_int64(obj->writer, l));
        }
 }
-ZEND_METHOD(ion_Writer_Writer, writeFloat)
+static ZEND_METHOD(ion_Writer_Writer, writeFloat)
 {
        php_ion_writer *obj = php_ion_obj(writer, Z_OBJ_P(ZEND_THIS));
        OBJ_CHECK(obj);
@@ -985,7 +1344,7 @@ ZEND_METHOD(ion_Writer_Writer, writeFloat)
 
        ION_CHECK(ion_writer_write_double(obj->writer, d));
 }
-ZEND_METHOD(ion_Writer_Writer, writeDecimal)
+static ZEND_METHOD(ion_Writer_Writer, writeDecimal)
 {
        php_ion_writer *obj = php_ion_obj(writer, Z_OBJ_P(ZEND_THIS));
        OBJ_CHECK(obj);
@@ -1004,7 +1363,7 @@ ZEND_METHOD(ion_Writer_Writer, writeDecimal)
                ION_CHECK(ion_writer_write_ion_decimal(obj->writer, &dec->dec));
        }
 }
-ZEND_METHOD(ion_Writer_Writer, writeTimestamp)
+static ZEND_METHOD(ion_Writer_Writer, writeTimestamp)
 {
        php_ion_writer *obj = php_ion_obj(writer, Z_OBJ_P(ZEND_THIS));
        OBJ_CHECK(obj);
@@ -1031,7 +1390,7 @@ ZEND_METHOD(ion_Writer_Writer, writeTimestamp)
        }
        ION_CHECK(ion_writer_write_timestamp(obj->writer, &tmp));
 }
-ZEND_METHOD(ion_Writer_Writer, writeSymbol)
+static ZEND_METHOD(ion_Writer_Writer, writeSymbol)
 {
        php_ion_writer *obj = php_ion_obj(writer, Z_OBJ_P(ZEND_THIS));
        OBJ_CHECK(obj);
@@ -1051,7 +1410,7 @@ ZEND_METHOD(ion_Writer_Writer, writeSymbol)
                ION_CHECK(ion_writer_write_ion_symbol(obj->writer, &sym->sym));
        }
 }
-ZEND_METHOD(ion_Writer_Writer, writeString)
+static ZEND_METHOD(ion_Writer_Writer, writeString)
 {
        php_ion_writer *obj = php_ion_obj(writer, Z_OBJ_P(ZEND_THIS));
        OBJ_CHECK(obj);
@@ -1064,7 +1423,7 @@ ZEND_METHOD(ion_Writer_Writer, writeString)
        ION_STRING is;
        ION_CHECK(ion_writer_write_string(obj->writer, ion_string_from_zend(&is, str)));
 }
-ZEND_METHOD(ion_Writer_Writer, writeCLob)
+static ZEND_METHOD(ion_Writer_Writer, writeCLob)
 {
        php_ion_writer *obj = php_ion_obj(writer, Z_OBJ_P(ZEND_THIS));
        OBJ_CHECK(obj);
@@ -1076,7 +1435,7 @@ ZEND_METHOD(ion_Writer_Writer, writeCLob)
 
        ION_CHECK(ion_writer_write_clob(obj->writer, (BYTE *) str->val, str->len));
 }
-ZEND_METHOD(ion_Writer_Writer, writeBLob)
+static ZEND_METHOD(ion_Writer_Writer, writeBLob)
 {
        php_ion_writer *obj = php_ion_obj(writer, Z_OBJ_P(ZEND_THIS));
        OBJ_CHECK(obj);
@@ -1088,7 +1447,7 @@ ZEND_METHOD(ion_Writer_Writer, writeBLob)
 
        ION_CHECK(ion_writer_write_blob(obj->writer, (BYTE *) str->val, str->len));
 }
-ZEND_METHOD(ion_Writer_Writer, startLob)
+static ZEND_METHOD(ion_Writer_Writer, startLob)
 {
        php_ion_writer *obj = php_ion_obj(writer, Z_OBJ_P(ZEND_THIS));
        OBJ_CHECK(obj);
@@ -1102,7 +1461,7 @@ ZEND_METHOD(ion_Writer_Writer, startLob)
        OBJ_CHECK(typ);
        ION_CHECK(ion_writer_start_lob(obj->writer, php_ion_obj(type, typ)->typ));
 }
-ZEND_METHOD(ion_Writer_Writer, appendLob)
+static ZEND_METHOD(ion_Writer_Writer, appendLob)
 {
        php_ion_writer *obj = php_ion_obj(writer, Z_OBJ_P(ZEND_THIS));
        OBJ_CHECK(obj);
@@ -1114,7 +1473,7 @@ ZEND_METHOD(ion_Writer_Writer, appendLob)
 
        ION_CHECK(ion_writer_append_lob(obj->writer, (BYTE *) str->val, str->len));
 }
-ZEND_METHOD(ion_Writer_Writer, finishLob)
+static ZEND_METHOD(ion_Writer_Writer, finishLob)
 {
        php_ion_writer *obj = php_ion_obj(writer, Z_OBJ_P(ZEND_THIS));
        OBJ_CHECK(obj);
@@ -1123,7 +1482,7 @@ ZEND_METHOD(ion_Writer_Writer, finishLob)
 
        ION_CHECK(ion_writer_finish_lob(obj->writer));
 }
-ZEND_METHOD(ion_Writer_Writer, startContainer)
+static ZEND_METHOD(ion_Writer_Writer, startContainer)
 {
        php_ion_writer *obj = php_ion_obj(writer, Z_OBJ_P(ZEND_THIS));
        OBJ_CHECK(obj);
@@ -1137,7 +1496,7 @@ ZEND_METHOD(ion_Writer_Writer, startContainer)
        OBJ_CHECK(typ);
        ION_CHECK(ion_writer_start_container(obj->writer, php_ion_obj(type, typ)->typ));
 }
-ZEND_METHOD(ion_Writer_Writer, finishContainer)
+static ZEND_METHOD(ion_Writer_Writer, finishContainer)
 {
        php_ion_writer *obj = php_ion_obj(writer, Z_OBJ_P(ZEND_THIS));
        OBJ_CHECK(obj);
@@ -1146,7 +1505,7 @@ ZEND_METHOD(ion_Writer_Writer, finishContainer)
 
        ION_CHECK(ion_writer_finish_container(obj->writer));
 }
-ZEND_METHOD(ion_Writer_Writer, writeFieldName)
+static ZEND_METHOD(ion_Writer_Writer, writeFieldName)
 {
        php_ion_writer *obj = php_ion_obj(writer, Z_OBJ_P(ZEND_THIS));
        OBJ_CHECK(obj);
@@ -1166,7 +1525,7 @@ ZEND_METHOD(ion_Writer_Writer, writeFieldName)
                ION_CHECK(ion_writer_write_field_name_symbol(obj->writer, &sym->sym));
        }
 }
-ZEND_METHOD(ion_Writer_Writer, writeAnnotation)
+static ZEND_METHOD(ion_Writer_Writer, writeAnnotation)
 {
        php_ion_writer *obj = php_ion_obj(writer, Z_OBJ_P(ZEND_THIS));
        OBJ_CHECK(obj);
@@ -1190,7 +1549,7 @@ ZEND_METHOD(ion_Writer_Writer, writeAnnotation)
                }
        }
 }
-ZEND_METHOD(ion_Writer_Writer, getDepth)
+static ZEND_METHOD(ion_Writer_Writer, getDepth)
 {
        php_ion_writer *obj = php_ion_obj(writer, Z_OBJ_P(ZEND_THIS));
        OBJ_CHECK(obj);
@@ -1201,7 +1560,7 @@ ZEND_METHOD(ion_Writer_Writer, getDepth)
        ION_CHECK(ion_writer_get_depth(obj->writer, &depth));
        RETURN_LONG(depth);
 }
-ZEND_METHOD(ion_Writer_Writer, flush)
+static ZEND_METHOD(ion_Writer_Writer, flush)
 {
        php_ion_writer *obj = php_ion_obj(writer, Z_OBJ_P(ZEND_THIS));
        OBJ_CHECK(obj);
@@ -1215,7 +1574,7 @@ ZEND_METHOD(ion_Writer_Writer, flush)
        }
        RETURN_LONG(flushed);
 }
-ZEND_METHOD(ion_Writer_Writer, finish)
+static ZEND_METHOD(ion_Writer_Writer, finish)
 {
        php_ion_writer *obj = php_ion_obj(writer, Z_OBJ_P(ZEND_THIS));
        OBJ_CHECK(obj);
@@ -1229,13 +1588,13 @@ ZEND_METHOD(ion_Writer_Writer, finish)
        }
        RETURN_LONG(flushed);
 }
-ZEND_METHOD(ion_Writer_Writer, writeOne)
+static ZEND_METHOD(ion_Writer_Writer, writeOne)
 {
 }
-ZEND_METHOD(ion_Writer_Writer, writeAll)
+static ZEND_METHOD(ion_Writer_Writer, writeAll)
 {
 }
-ZEND_METHOD(ion_Writer_Buffer_Writer, __construct)
+static ZEND_METHOD(ion_Writer_Buffer_Writer, __construct)
 {
        php_ion_writer *obj = php_ion_obj(writer, Z_OBJ_P(ZEND_THIS));
        PTR_CHECK(obj);
@@ -1253,7 +1612,7 @@ ZEND_METHOD(ion_Writer_Buffer_Writer, __construct)
 
        php_ion_writer_ctor(obj);
 }
-ZEND_METHOD(ion_Writer_Buffer_Writer, getBuffer)
+static ZEND_METHOD(ion_Writer_Buffer_Writer, getBuffer)
 {
        php_ion_writer *obj = php_ion_obj(writer, Z_OBJ_P(ZEND_THIS));
        OBJ_CHECK(obj);
@@ -1262,7 +1621,7 @@ ZEND_METHOD(ion_Writer_Buffer_Writer, getBuffer)
 
        RETVAL_STR(zend_string_dup(obj->buffer.str.s, 0));
 }
-ZEND_METHOD(ion_Writer_Stream_Writer, __construct)
+static ZEND_METHOD(ion_Writer_Stream_Writer, __construct)
 {
        php_ion_writer *obj = php_ion_obj(writer, Z_OBJ_P(ZEND_THIS));
        PTR_CHECK(obj);
@@ -1279,7 +1638,7 @@ ZEND_METHOD(ion_Writer_Stream_Writer, __construct)
 
        php_ion_writer_ctor(obj);
 }
-ZEND_METHOD(ion_Writer_Stream_Writer, getStream)
+static ZEND_METHOD(ion_Writer_Stream_Writer, getStream)
 {
        php_ion_writer *obj = php_ion_obj(writer, Z_OBJ_P(ZEND_THIS));
        OBJ_CHECK(obj);
@@ -1291,31 +1650,24 @@ ZEND_METHOD(ion_Writer_Stream_Writer, getStream)
        RETURN_RES(obj->stream.ptr->res);
 }
 
-ZEND_METHOD(ion_PHP_Serializer, __construct)
+static ZEND_METHOD(ion_Serializer_PHP, __construct)
 {
-       zend_object *obj = Z_OBJ_P(ZEND_THIS);
+       php_ion_serializer_php *obj = php_ion_obj(serializer_php, Z_OBJ_P(ZEND_THIS));
+       PTR_CHECK(obj);
+
+       obj->serializer.call_magic = true;
 
-       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)
+       ZEND_PARSE_PARAMETERS_START(0, 3)
                Z_PARAM_OPTIONAL
-               Z_PARAM_BOOL(call_magic)
-               Z_PARAM_STR_OR_NULL(call_custom)
+               Z_PARAM_OBJ_OF_CLASS_OR_NULL(obj->opt, ce_Writer_Options)
+               Z_PARAM_BOOL(obj->serializer.multi_seq)
+               Z_PARAM_BOOL(obj->serializer.call_magic)
+               Z_PARAM_STR_OR_NULL(obj->serializer.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"));
-       }
+       php_ion_serializer_php_ctor(obj);
 }
-ZEND_METHOD(ion_PHP_Serializer, __invoke)
+static ZEND_METHOD(ion_Serializer_PHP, __invoke)
 {
        zend_object *obj = Z_OBJ_P(ZEND_THIS);
 
@@ -1324,18 +1676,32 @@ ZEND_METHOD(ion_PHP_Serializer, __invoke)
                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);
-               }
+       if (obj->ce == ce_Serializer_PHP) {
+               // default, fast path
+               php_ion_serialize(&php_ion_obj(serializer_php, obj)->serializer, data, return_value);
+       } else {
+               zend_call_method_with_1_params(obj, obj->ce, NULL /* TODO */, "serialize", return_value, data);
        }
 }
-ZEND_METHOD(ion_PHP_Serializer, serialize)
+static ZEND_FUNCTION(ion_serialize)
+{
+       zval *data;
+       zend_object *zo_ser = NULL;
+       ZEND_PARSE_PARAMETERS_START(1, 2)
+               Z_PARAM_ZVAL(data)
+               Z_PARAM_OPTIONAL
+               Z_PARAM_OBJ_OF_CLASS_OR_NULL(zo_ser, ce_Serializer)
+       ZEND_PARSE_PARAMETERS_END();
+
+       if (!zo_ser || zo_ser->ce == ce_Serializer_PHP) {
+               // default, fast path
+               php_ion_serializer *ser = zo_ser ? &php_ion_obj(serializer_php, zo_ser)->serializer : NULL;
+               php_ion_serialize(ser, data, return_value);
+       } else {
+               zend_call_method_with_1_params(zo_ser, NULL, NULL, "__invoke", return_value, data);
+       }
+}
+static ZEND_METHOD(ion_Serializer_PHP, serialize)
 {
        //zend_object *obj = Z_OBJ_P(ZEND_THIS);
 
@@ -1345,96 +1711,70 @@ ZEND_METHOD(ion_PHP_Serializer, serialize)
        ZEND_PARSE_PARAMETERS_END();
 
        // TODO
+       zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Not implemented");
 }
-ZEND_METHOD(ion_PHP_Unserializer, __construct)
+
+static ZEND_METHOD(ion_Unserializer_PHP, __construct)
 {
-       zend_object *obj = Z_OBJ_P(ZEND_THIS);
+       php_ion_unserializer_php *obj = php_ion_obj(unserializer_php, Z_OBJ_P(ZEND_THIS));
+       PTR_CHECK(obj);
+
+       obj->unserializer.call_magic = true;
 
-       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)
+       ZEND_PARSE_PARAMETERS_START(0, 3)
                Z_PARAM_OPTIONAL
-               Z_PARAM_BOOL(call_magic)
-               Z_PARAM_STR_OR_NULL(call_custom)
+               Z_PARAM_OBJ_OF_CLASS_OR_NULL(obj->opt, ce_Reader_Options)
+               Z_PARAM_BOOL(obj->unserializer.multi_seq)
+               Z_PARAM_BOOL(obj->unserializer.call_magic)
+               Z_PARAM_STR_OR_NULL(obj->unserializer.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"));
-       }
+       php_ion_unserializer_php_ctor(obj);
 }
-ZEND_METHOD(ion_PHP_Unserializer, __invoke)
+static ZEND_METHOD(ion_Unserializer_PHP, __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();
+       zval *data;
+       ZEND_PARSE_PARAMETERS_START(1, 1)
+               Z_PARAM_ZVAL(data)
+       ZEND_PARSE_PARAMETERS_END();
 
-       // TODO
+       if (obj->ce == ce_Unserializer_PHP) {
+               php_ion_unserialize(&php_ion_obj(unserializer_php, obj)->unserializer, data, return_value);
+       } else {
+               zend_call_method_with_1_params(obj, obj->ce, NULL /* TODO */, "unserialize", return_value, data);
+       }
 }
-
-ZEND_FUNCTION(ion_serialize)
+static ZEND_FUNCTION(ion_unserialize)
 {
-       zval *zv;
-       zend_object *zo_opt = NULL;
+       zval *data;
+       zend_object *zo_ser = NULL;
        ZEND_PARSE_PARAMETERS_START(1, 2)
-               Z_PARAM_ZVAL(zv)
+               Z_PARAM_ZVAL(data)
                Z_PARAM_OPTIONAL
-               Z_PARAM_OBJ_OF_CLASS_OR_NULL(zo_opt, ce_Writer_Options)
+               Z_PARAM_OBJ_OF_CLASS_OR_NULL(zo_ser, ce_Unserializer)
        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;
-       obj->php.call_magic_serialize = true;
-       php_ion_writer_ctor(obj);
-
-       php_ion_serialize(obj, zv, return_value);
-       zval_ptr_dtor(&zwriter);
+       if (!zo_ser || zo_ser->ce == ce_Unserializer_PHP) {
+               // default, fast path
+               php_ion_unserializer *ser = zo_ser ? &php_ion_obj(unserializer_php, zo_ser)->unserializer : NULL;
+               php_ion_unserialize(ser, data, return_value);
+       } else {
+               zend_call_method_with_1_params(zo_ser, NULL, NULL, "__invoke", return_value, data);
+       }
 }
-ZEND_FUNCTION(ion_unserialize)
+static ZEND_METHOD(ion_Unserializer_PHP, 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();
+       //zend_object *obj = Z_OBJ_P(ZEND_THIS);
 
-       zval zreader;
-       object_init_ex(&zreader, ce_Reader_Buffer_Reader);
+       zval *data;
+       ZEND_PARSE_PARAMETERS_START(1, 1)
+               Z_PARAM_ZVAL(data)
+       ZEND_PARSE_PARAMETERS_END();
 
-       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);
-       obj->php.call_magic_unserialize = true;
-       php_ion_reader_ctor(obj);
-       php_ion_unserialize(obj, NULL, return_value);
-       zval_ptr_dtor(&zreader);
+       // TODO
+       zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Not implemented");
 }
 
 PHP_RINIT_FUNCTION(ion)
@@ -1443,6 +1783,7 @@ PHP_RINIT_FUNCTION(ion)
        ZEND_TSRMLS_CACHE_UPDATE();
 #endif
 
+       php_ion_globals_symbols_init();
        php_ion_globals_serializer_init();
        php_ion_globals_unserializer_init();
        return SUCCESS;
@@ -1450,67 +1791,90 @@ PHP_RINIT_FUNCTION(ion)
 
 PHP_RSHUTDOWN_FUNCTION(ion)
 {
-       php_ion_globals_serializer_dtor();
        php_ion_globals_unserializer_dtor();
+       php_ion_globals_serializer_dtor();
+       php_ion_globals_symbols_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));
+#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"
 
 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();
+       // true globals
+       if (SUCCESS != g_sym_init())  {
+               return FAILURE;
+       }
 
-       ce_Collection = register_class_ion_Collection();
+       // Catalog
+       php_ion_register(catalog, Catalog, zend_ce_countable);
 
+       // Decimal
        php_ion_register(decimal, Decimal);
        php_ion_register(decimal_ctx, Decimal_Context);
-       php_ion_register(timestamp, Timestamp, php_date_get_date_ce());
-       ce_Timestamp_Precision = register_class_ion_Timestamp_Precision();
-       php_ion_register(catalog, Catalog);
+       ce_Decimal_Context_Rounding = register_class_ion_Decimal_Context_Rounding();
 
-       ce_Reader = register_class_ion_Reader(spl_ce_RecursiveIterator);
+       // LOB
+       ce_LOB = register_class_ion_LOB();
 
+       // Reader
+       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();
+       // Serializer
+       ce_Serializer = register_class_ion_Serializer();
+       php_ion_register(serializer_php, Serializer_PHP, ce_Serializer);
 
+       // Symbol
+       php_ion_register(symbol, Symbol);
+       oh_Symbol.compare = php_ion_symbol_zval_compare;
+       php_ion_register(symbol_iloc, Symbol_ImportLocation);
+       php_ion_register(symbol_table, Symbol_Table);
+       ce_Symbol_Table->create_object = NULL;
+       ce_Symbol_Table_Local = register_class_ion_Symbol_Table_Local(ce_Symbol_Table);
+       ce_Symbol_Table_Local->create_object = create_ion_Symbol_Table;
+       ce_Symbol_Table_Shared = register_class_ion_Symbol_Table_Shared(ce_Symbol_Table);
+       ce_Symbol_Table_Shared->create_object = create_ion_Symbol_Table;
+       ce_Symbol_Enum = register_class_ion_Symbol_Enum();
+       ce_Symbol_Table_System = register_class_ion_Symbol_Table_System(ce_Symbol_Enum);
+       ce_Symbol_Table_PHP = register_class_ion_Symbol_Table_PHP(ce_Symbol_Enum);
+
+       // Timestamp
+       ce_Timestamp = register_class_ion_Timestamp(php_date_get_date_ce());
+       ce_Timestamp_Precision = register_class_ion_Timestamp_Precision();
+
+       // Type
+       php_ion_register(type, Type);
+
+       // Writer
+       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();
+       // Unserializer
+       ce_Unserializer = register_class_ion_Unserializer();
+       php_ion_register(unserializer_php, Unserializer_PHP, ce_Unserializer);
+
+       return SUCCESS;
+}
 
+PHP_MSHUTDOWN_FUNCTION(ion)
+{
+       if (g_sym_tab_php) {
+               ion_symbol_table_close(g_sym_tab_php);
+       }
+       zend_hash_destroy(&g_sym_hash);
        return SUCCESS;
 }
 
@@ -1524,12 +1888,18 @@ PHP_MINFO_FUNCTION(ion)
 PHP_GINIT_FUNCTION(ion)
 {
        memset(ion_globals, 0, sizeof(*ion_globals));
+
+       php_ion_decimal_ctx_init_max(&ion_globals->decimal.ctx, DEC_ROUND_HALF_EVEN);
+       php_ion_decimal_from_zend_long(&ion_globals->decimal.zend_max, &ion_globals->decimal.ctx, ZEND_LONG_MAX);
+       php_ion_decimal_from_zend_long(&ion_globals->decimal.zend_min, &ion_globals->decimal.ctx, ZEND_LONG_MIN);
 }
+
 PHP_GSHUTDOWN_FUNCTION(ion)
 {
 }
 
 static zend_module_dep ion_module_deps[] = {
+       ZEND_MOD_REQUIRED("date")
        ZEND_MOD_REQUIRED("spl")
        ZEND_MOD_END
 };
@@ -1541,7 +1911,7 @@ zend_module_entry ion_module_entry = {
        "ion",                                  /* Extension name */
        ext_functions,                  /* zend_function_entry */
        PHP_MINIT(ion),                 /* PHP_MINIT - Module initialization */
-       NULL,                                   /* PHP_MSHUTDOWN - Module shutdown */
+       PHP_MSHUTDOWN(ion),             /* 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 */