From ebedd44165b65ea28405d41e50757182cf90f658 Mon Sep 17 00:00:00 2001 From: Michael Wallner Date: Mon, 20 Dec 2021 16:43:43 +0100 Subject: [PATCH] improve shared symbol tables --- ion.c | 18 +++- ion.stub.php | 7 +- ion_arginfo.h | 15 +-- ion_private.h | 159 +++++++++++++++++++---------- tests/Symbol/Shared/roundtrip.phpt | 47 +++++++++ tests/Symbol/Table.phpt | 10 +- 6 files changed, 187 insertions(+), 69 deletions(-) create mode 100644 tests/Symbol/Shared/roundtrip.phpt diff --git a/ion.c b/ion.c index 3dc454a..1426172 100644 --- a/ion.c +++ b/ion.c @@ -176,10 +176,12 @@ static ZEND_METHOD(ion_Symbol_Table_Shared, __construct) zend_string *zname; zend_long version = 1; - ZEND_PARSE_PARAMETERS_START(1, 2) + HashTable *ht_sym = NULL; + ZEND_PARSE_PARAMETERS_START(1, 3) Z_PARAM_STR(zname) Z_PARAM_OPTIONAL Z_PARAM_LONG(version) + Z_PARAM_ARRAY_HT_OR_NULL(ht_sym) ZEND_PARSE_PARAMETERS_END(); ION_CHECK(ion_symbol_table_open_with_type(&obj->tab, NULL, ist_SHARED)); @@ -190,6 +192,20 @@ static ZEND_METHOD(ion_Symbol_Table_Shared, __construct) ION_CHECK(ion_symbol_table_set_version(obj->tab, version)); php_ion_symbol_table_ctor(obj); + + zval *zsym; + if (ht_sym) ZEND_HASH_FOREACH_VAL(ht_sym, zsym) + { + zend_string *str = zval_get_string(zsym); + if (EG(exception)) { + break; + } + + ION_STRING is; + ION_CHECK(ion_symbol_table_add_symbol(obj->tab, ion_string_from_zend(&is, str), NULL), zend_string_release(str)); + zend_string_release(str); + } + ZEND_HASH_FOREACH_END(); } static ZEND_METHOD(ion_Symbol_Table, getMaxId) { diff --git a/ion.stub.php b/ion.stub.php index e7a977d..2e33243 100644 --- a/ion.stub.php +++ b/ion.stub.php @@ -131,13 +131,15 @@ class Local implements \ion\Symbol\Table { namespace ion\Symbol\Table; class Shared implements \ion\Symbol\Table { - private array $symbols = []; - public function __construct( public readonly string $name, public readonly int $version = 1, + ?array $symbols = null, ) {} + /** Internal cache */ + private array $symbols = []; + /** @alias ion\Symbol\Table::getMaxId */ public function getMaxId() : int {} @@ -151,6 +153,7 @@ class Shared implements \ion\Symbol\Table { namespace ion; class Catalog implements Countable { + /** Internal cache */ private array $symbolTables = []; public function __construct() {} diff --git a/ion_arginfo.h b/ion_arginfo.h index 3a94e5f..867de8b 100644 --- a/ion_arginfo.h +++ b/ion_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 943d369644718978b3b990f4345d51bed5f71366 */ + * Stub hash: 8f4964120dccb00c053325183fda763f64f08b6e */ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_ion_Symbol_Table_PHP, 0, 0, ion\\Symbol\\Table, 0) ZEND_END_ARG_INFO() @@ -86,6 +86,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_ion_Symbol_Table_Shared___construct, 0, 0, 1) ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, version, IS_LONG, 0, "1") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, symbols, IS_ARRAY, 1, "null") ZEND_END_ARG_INFO() #define arginfo_class_ion_Symbol_Table_Shared_getMaxId arginfo_class_ion_Symbol_Enum_toSID @@ -1297,12 +1298,6 @@ static zend_class_entry *register_class_ion_Symbol_Table_Shared(zend_class_entry class_entry = zend_register_internal_class_ex(&ce, NULL); zend_class_implements(class_entry, 1, class_entry_ion_Symbol_Table); - zval property_symbols_default_value; - ZVAL_EMPTY_ARRAY(&property_symbols_default_value); - zend_string *property_symbols_name = zend_string_init("symbols", sizeof("symbols") - 1, 1); - zend_declare_typed_property(class_entry, property_symbols_name, &property_symbols_default_value, ZEND_ACC_PRIVATE, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_ARRAY)); - zend_string_release(property_symbols_name); - zval property_name_default_value; ZVAL_UNDEF(&property_name_default_value); zend_string *property_name_name = zend_string_init("name", sizeof("name") - 1, 1); @@ -1315,6 +1310,12 @@ static zend_class_entry *register_class_ion_Symbol_Table_Shared(zend_class_entry zend_declare_typed_property(class_entry, property_version_name, &property_version_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); zend_string_release(property_version_name); + zval property_symbols_default_value; + ZVAL_EMPTY_ARRAY(&property_symbols_default_value); + zend_string *property_symbols_name = zend_string_init("symbols", sizeof("symbols") - 1, 1); + zend_declare_typed_property(class_entry, property_symbols_name, &property_symbols_default_value, ZEND_ACC_PRIVATE, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_ARRAY)); + zend_string_release(property_symbols_name); + return class_entry; } diff --git a/ion_private.h b/ion_private.h index 342b164..5264b0b 100644 --- a/ion_private.h +++ b/ion_private.h @@ -17,26 +17,43 @@ #include "ionc/ion.h" #define PHP_ION_SYMBOL_TABLE_VERSION 1 -#define PHP_ION_SYMBOL(e, s) {0, {sizeof(e)-1, (BYTE *) e}, {sizeof(s)-1, (BYTE *) s}}, +#define PHP_ION_SYMBOL(c, s) { \ + { \ + 0, \ + { sizeof(s)-1, (BYTE *) s }, \ + { { 0, NULL }, 0 }, \ + 0 \ + }, \ + { sizeof(c)-1, (BYTE *) c } \ +}, typedef struct php_ion_global_symbol { - SID id; + ION_SYMBOL s; ION_STRING e; - ION_STRING s; } php_ion_global_symbol; static php_ion_global_symbol g_sym_tab_php_sym[] = { - PHP_ION_SYMBOL("PHP", "PHP") - PHP_ION_SYMBOL("Reference", "R") - PHP_ION_SYMBOL("Backref", "r") - PHP_ION_SYMBOL("Property", "p") - PHP_ION_SYMBOL("ClassObject", "c") - PHP_ION_SYMBOL("CustomObject", "C") - PHP_ION_SYMBOL("Object", "o") - PHP_ION_SYMBOL("MagicObject", "O") - PHP_ION_SYMBOL("Serializable", "S") +#define PHP_ION_SYMBOL_PHP (g_sym_tab_php_sym[0]).s + PHP_ION_SYMBOL("PHP", "PHP") +#define PHP_ION_SYMBOL_REFERENCE (g_sym_tab_php_sym[1]).s + PHP_ION_SYMBOL("Reference", "R") +#define PHP_ION_SYMBOL_BACKREF (g_sym_tab_php_sym[2]).s + PHP_ION_SYMBOL("Backref", "r") +#define PHP_ION_SYMBOL_PROPERTY (g_sym_tab_php_sym[3]).s + PHP_ION_SYMBOL("Property", "p") +#define PHP_ION_SYMBOL_CLASS_OBJECT (g_sym_tab_php_sym[4]).s + PHP_ION_SYMBOL("ClassObject", "c") +#define PHP_ION_SYMBOL_CUSTOM_OBJECT (g_sym_tab_php_sym[5]).s + PHP_ION_SYMBOL("CustomObject", "C") +#define PHP_ION_SYMBOL_OBJECT (g_sym_tab_php_sym[6]).s + PHP_ION_SYMBOL("Object", "o") +#define PHP_ION_SYMBOL_MAGIC_OBJECT (g_sym_tab_php_sym[7]).s + PHP_ION_SYMBOL("MagicObject", "O") +#define PHP_ION_SYMBOL_SERIALIZEABLE (g_sym_tab_php_sym[8]).s + PHP_ION_SYMBOL("Serializable", "S") +#define PHP_ION_SYMBOL_ENUM (g_sym_tab_php_sym[9]).s PHP_ION_SYMBOL("Enum", "E") - {0} + {{0}, {0}} }; #undef PHP_ION_SYMBOL @@ -94,11 +111,11 @@ static inline int g_sym_init(void) } php_ion_global_symbol *ptr = g_sym_tab_php_sym; ion_symbol_table_set_version(g_sym_tab_php, PHP_ION_SYMBOL_TABLE_VERSION); - ion_symbol_table_set_name(g_sym_tab_php, &ptr->s); + ion_symbol_table_set_name(g_sym_tab_php, &ptr->s.value); while (ptr->e.value) { - ion_symbol_table_add_symbol(g_sym_tab_php, &ptr->s, &ptr->id); - g_sym_hash_add(sys_max_id + ptr->id, (const char *) ptr->s.value, ptr->s.length); - g_sym_map_add(sys_max_id + ptr->id, (const char *) ptr->e.value, ptr->e.length); + ion_symbol_table_add_symbol(g_sym_tab_php, &ptr->s.value, &ptr->s.sid); + g_sym_hash_add(sys_max_id + ptr->s.sid, (const char *) ptr->s.value.value, ptr->s.value.length); + g_sym_map_add(sys_max_id + ptr->s.sid, (const char *) ptr->e.value, ptr->e.length); ++ptr; } ion_symbol_table_lock(g_sym_tab_php); @@ -120,6 +137,7 @@ typedef struct php_ion_serializer { } php_ion_serializer; typedef struct php_ion_annotaions { + uint8_t shared_symtab:1; uint8_t backref:1; uint8_t makeref:1; uint8_t object_prop:1; @@ -519,6 +537,27 @@ static inline void php_ion_symbol_zval(ION_SYMBOL *sym_ptr, zval *return_value) } } +static inline zval *php_ion_global_symbol_fetch_by_enum(zend_string *name) +{ + zval *zgs = zend_hash_find(&php_ion_globals.symbol.cache, name); + if (!zgs) { + zval *zid = zend_hash_find(&g_sym_map, name); + if (zid) { + zval *zss = zend_hash_index_find(&g_sym_hash, Z_LVAL_P(zid)); + if (zss) { + zval zsym; + object_init_ex(&zsym, ce_Symbol); + php_ion_symbol *sym = php_ion_obj(symbol, Z_OBJ(zsym)); + sym->sym.sid = Z_LVAL_P(zid); + sym->value = zval_get_string(zss); + php_ion_symbol_ctor(sym); + zgs = zend_hash_add(&php_ion_globals.symbol.cache, name, &zsym); + } + } + } + return zgs; +} + php_ion_decl(symbol, Symbol, php_ion_symbol_dtor(obj)); typedef struct php_ion_symbol_table { @@ -927,8 +966,6 @@ static inline void php_ion_catalog_symbol_table_zval(php_ion_catalog *obj, ION_S if (ztab) { zend_string_release(key); RETURN_COPY(ztab); - } else { - fprintf(stderr, "FAIL\n"); } } @@ -1007,6 +1044,7 @@ static inline iERR on_context_change(void *context, ION_COLLECTION *imports) php_ion_reader *obj = php_ion_obj(reader, context); (void) obj; } + fprintf(stderr, "%s\n", __FUNCTION__); return IERR_OK; } @@ -1267,7 +1305,7 @@ static inline void php_ion_serializer_php_dtor(php_ion_serializer_php *obj) static inline void php_ion_serialize_zval(php_ion_serializer *, zval *); -static inline void php_ion_serialize_struct(php_ion_serializer *ser, zend_array *arr, bool props) +static inline void php_ion_serialize_struct(php_ion_serializer *ser, zend_array *arr, bool unmangle_props, bool annotate_props) { ION_CHECK(ion_writer_start_container(ser->writer, tid_STRUCT)); @@ -1275,20 +1313,23 @@ static inline void php_ion_serialize_struct(php_ion_serializer *ser, zend_array zend_ulong h; zend_string *k = NULL; if (arr) ZEND_HASH_FOREACH_KEY_VAL_IND(arr, h, k, v) + char buf[MAX_LENGTH_OF_LONG + 1]; ION_STRING is; if (k) { size_t prop_len; const char *class_name, *prop_name; - if (props && (SUCCESS == zend_unmangle_property_name_ex(k, &class_name, &prop_name, &prop_len)) && class_name) { - ION_CHECK(ion_writer_add_annotation(ser->writer, ion_string_assign_cstr(&is, ZEND_STRL("p")))); - ION_CHECK(ion_writer_add_annotation(ser->writer, ion_string_assign_cstr(&is, (char *) class_name, prop_name - class_name - 1))); + if (unmangle_props && (SUCCESS == zend_unmangle_property_name_ex(k, &class_name, &prop_name, &prop_len)) && class_name) { + if (annotate_props) { + ION_CHECK(ion_writer_add_annotation_symbol(ser->writer, &PHP_ION_SYMBOL_PROPERTY)); + ION_CHECK(ion_writer_add_annotation(ser->writer, ion_string_assign_cstr(&is, (char *) class_name, prop_name - class_name - 1))); + } } else { prop_name = k->val; prop_len = k->len; } ion_string_assign_cstr(&is, (char *) prop_name, prop_len); } else { - char buf[MAX_LENGTH_OF_LONG + 1], *end = buf + sizeof(buf) - 1; + char *end = buf + sizeof(buf) - 1; char *ptr = zend_print_long_to_buf(end, (zend_long) h); ion_string_assign_cstr(&is, ptr, end - ptr); } @@ -1323,7 +1364,7 @@ static inline void php_ion_serialize_array(php_ion_serializer *ser, zend_array * if (zend_array_is_list(arr)) { php_ion_serialize_list(ser, arr); } else { - php_ion_serialize_struct(ser, arr, false); + php_ion_serialize_struct(ser, arr, false, false); } } @@ -1336,7 +1377,7 @@ static inline void php_ion_serialize_object_iface(php_ion_serializer *ser, zend_ ZVAL_OBJ(&tmp, zobject); if (SUCCESS == zobject->ce->serialize(&tmp, &buf, &len, NULL)) { ION_STRING is; - ION_CHECK(ion_writer_add_annotation(ser->writer, ion_string_assign_cstr(&is, ZEND_STRL("S")))); + ION_CHECK(ion_writer_add_annotation_symbol(ser->writer, &PHP_ION_SYMBOL_SERIALIZEABLE)); ION_CHECK(ion_writer_add_annotation(ser->writer, ion_string_from_zend(&is, zobject->ce->name))); ION_CHECK(ion_writer_write_string(ser->writer, ion_string_assign_cstr(&is, (char *) buf, len))); efree(buf); @@ -1356,7 +1397,7 @@ static inline void php_ion_serialize_object_magic(php_ion_serializer *ser, zend_ if (IS_ARRAY == Z_TYPE(rv)) { ION_STRING is; - ION_CHECK(ion_writer_add_annotation(ser->writer, ion_string_assign_cstr(&is, fn ? "C" : "O", 1))); + ION_CHECK(ion_writer_add_annotation_symbol(ser->writer, fn ? &PHP_ION_SYMBOL_CUSTOM_OBJECT : &PHP_ION_SYMBOL_MAGIC_OBJECT)); ION_CHECK(ion_writer_add_annotation(ser->writer, ion_string_from_zend(&is, zobject->ce->name))); php_ion_serialize_zval(ser, &rv); zval_ptr_dtor(&rv); @@ -1371,7 +1412,7 @@ static inline void php_ion_serialize_object_magic(php_ion_serializer *ser, zend_ static inline void php_ion_serialize_object_enum(php_ion_serializer *ser, zend_object *zobject) { ION_STRING is; - ION_CHECK(ion_writer_add_annotation(ser->writer, ion_string_assign_cstr(&is, ZEND_STRL("E")))); + ION_CHECK(ion_writer_add_annotation_symbol(ser->writer, &PHP_ION_SYMBOL_ENUM)); ION_CHECK(ion_writer_add_annotation(ser->writer, ion_string_from_zend(&is, zobject->ce->name))); zval *z_cname = zend_enum_fetch_case_name(zobject); @@ -1383,17 +1424,17 @@ static inline void php_ion_serialize_object_std(php_ion_serializer *ser, zend_ob ION_STRING is; if (zobject->ce != zend_standard_class_def) { - ION_CHECK(ion_writer_add_annotation(ser->writer, ion_string_assign_cstr(&is, ZEND_STRL("c")))); + ION_CHECK(ion_writer_add_annotation_symbol(ser->writer, &PHP_ION_SYMBOL_CLASS_OBJECT)); ION_CHECK(ion_writer_add_annotation(ser->writer, ion_string_from_zend(&is, zobject->ce->name))); } else { - ION_CHECK(ion_writer_add_annotation(ser->writer, ion_string_assign_cstr(&is, ZEND_STRL("o")))); + ION_CHECK(ion_writer_add_annotation_symbol(ser->writer, &PHP_ION_SYMBOL_OBJECT)); } zval zobj; ZVAL_OBJ(&zobj, zobject); HashTable *props = zend_get_properties_for(&zobj, ZEND_PROP_PURPOSE_SERIALIZE); if (props) { - php_ion_serialize_struct(ser, props, true); + php_ion_serialize_struct(ser, props, true, true); zend_release_properties(props); } else { zend_throw_exception_ex(spl_ce_UnexpectedValueException, IERR_INTERNAL_ERROR, @@ -1474,15 +1515,33 @@ static inline void php_ion_serialize_object(php_ion_serializer *ser, zend_object } } +static inline bool php_ion_serialize_system_value(php_ion_serializer *ser, zval *zv) +{ + if (1 == php_ion_globals.serializer.level) { + if (Z_TYPE_P(zv) == IS_OBJECT) { + if (Z_OBJCE_P(zv) == ce_Symbol_Table_Shared) { + php_ion_symbol_table *obj = php_ion_obj(symbol_table, Z_OBJ_P(zv)); + ION_CHECK_RETURN(true, ion_symbol_table_unload(obj->tab, ser->writer)); + return true; + } + } + } + return false; +} + static inline void php_ion_serialize_refcounted(php_ion_serializer *ser, zval *zv) { + if (php_ion_serialize_system_value(ser, zv)) { + return; + } + zend_ulong idx = (zend_ulong) (uintptr_t) Z_COUNTED_P(zv); ION_STRING is; if (zend_hash_index_exists(ser->ids, idx)) { zval *num = zend_hash_index_find(ser->ids, idx); - ION_CHECK(ion_writer_add_annotation(ser->writer, ion_string_assign_cstr(&is, ZEND_STRL("r")))); + ION_CHECK(ion_writer_add_annotation_symbol(ser->writer, &PHP_ION_SYMBOL_BACKREF)); ION_CHECK(ion_writer_write_int64(ser->writer, Z_LVAL_P(num))); } else { zval num; @@ -1507,7 +1566,7 @@ static inline void php_ion_serialize_refcounted(php_ion_serializer *ser, zval *z break; case IS_REFERENCE: - ION_CHECK(ion_writer_add_annotation(ser->writer, ion_string_assign_cstr(&is, ZEND_STRL("R")))); + ION_CHECK(ion_writer_add_annotation_symbol(ser->writer, &PHP_ION_SYMBOL_REFERENCE)); php_ion_serialize_zval(ser, Z_REFVAL_P(zv)); break; } @@ -1914,8 +1973,16 @@ static inline void php_ion_unserialize_struct(php_ion_unserializer *ser, zval *r php_ion_unserialize_object(ser, return_value); } } else if (!ser->annotations.object_type) { + bool is_shared_symtab = ser->annotations.shared_symtab; array_init(return_value); php_ion_unserialize_hash(ser, return_value); + if (is_shared_symtab) { + zval zobj; + object_init_ex(&zobj, ce_Symbol_Table_Shared); + zend_call_known_function(Z_OBJCE(zobj)->constructor, Z_OBJ(zobj), Z_OBJCE(zobj), NULL, 0, NULL, Z_ARRVAL_P(return_value)); + zval_ptr_dtor(return_value); + RETURN_COPY_VALUE(&zobj); + } } else if (ser->annotations.object_type == 'o') { object_init(return_value); php_ion_unserialize_hash(ser, return_value); @@ -2039,6 +2106,11 @@ static inline void php_ion_unserialize_annotations(php_ion_unserializer *ser) ION_CHECK(ion_reader_get_an_annotation(ser->reader, i, &ann_str)); if (ann_str.length != 1) { + if (ann_str.length == ION_SYS_STRLEN_SHARED_SYMBOL_TABLE) { + if (!memcmp(ann_str.value, ION_SYMBOL_SHARED_SYMBOL_TABLE_BYTES, ION_SYS_STRLEN_SHARED_SYMBOL_TABLE)) { + ser->annotations.shared_symtab = true; + } + } continue; } @@ -2324,24 +2396,3 @@ void php_ion_unserialize(php_ion_unserializer *ser, zval *zdata, zval *return_va OBJ_RELEASE(zo_ser); } } - -static inline zval *php_ion_global_symbol_fetch_by_enum(zend_string *name) -{ - zval *zgs = zend_hash_find(&php_ion_globals.symbol.cache, name); - if (!zgs) { - zval *zid = zend_hash_find(&g_sym_map, name); - if (zid) { - zval *zss = zend_hash_index_find(&g_sym_hash, Z_LVAL_P(zid)); - if (zss) { - zval zsym; - object_init_ex(&zsym, ce_Symbol); - php_ion_symbol *sym = php_ion_obj(symbol, Z_OBJ(zsym)); - sym->sym.sid = Z_LVAL_P(zid); - sym->value = zval_get_string(zss); - php_ion_symbol_ctor(sym); - zgs = zend_hash_add(&php_ion_globals.symbol.cache, name, &zsym); - } - } - } - return zgs; -} diff --git a/tests/Symbol/Shared/roundtrip.phpt b/tests/Symbol/Shared/roundtrip.phpt new file mode 100644 index 0000000..c9e2b93 --- /dev/null +++ b/tests/Symbol/Shared/roundtrip.phpt @@ -0,0 +1,47 @@ +--TEST-- +Symbol\Table\Shared/roundtrip +--EXTENSIONS-- +ion +--FILE-- +TEST + +DONE +--EXPECTF-- +TEST +string(79) "$ion_shared_symbol_table::{name:"shared",version:1,symbols:["foo","bar","baz"]}" +object(ion\Symbol\Table\Shared)#%d (3) { + ["name"]=> + string(6) "shared" + ["version"]=> + int(1) + ["symbols":"ion\Symbol\Table\Shared":private]=> + array(0) { + } +} +DONE diff --git a/tests/Symbol/Table.phpt b/tests/Symbol/Table.phpt index f35bb10..86d4b05 100644 --- a/tests/Symbol/Table.phpt +++ b/tests/Symbol/Table.phpt @@ -49,22 +49,22 @@ DONE TEST global object(ion\Symbol\Table\Shared)#%d (3) { - ["symbols":"ion\Symbol\Table\Shared":private]=> - array(0) { - } ["name"]=> string(4) "$ion" ["version"]=> int(1) -} -object(ion\Symbol\Table\Shared)#%d (3) { ["symbols":"ion\Symbol\Table\Shared":private]=> array(0) { } +} +object(ion\Symbol\Table\Shared)#%d (3) { ["name"]=> string(3) "PHP" ["version"]=> int(1) + ["symbols":"ion\Symbol\Table\Shared":private]=> + array(0) { + } } object(ion\Symbol)#%d (3) { ["value"]=> -- 2.30.2