From 0969cb32a073bdd8ffaf1a647077ac690d430a8d Mon Sep 17 00:00:00 2001 From: Michael Wallner Date: Wed, 22 Dec 2021 11:06:05 +0100 Subject: [PATCH] improve catalog support; depends on amzn/ion-c#271 --- ion.c | 39 ++--- ion_private.h | 52 +++++- tests/Catalog.phpt | 3 + tests/Symbol/Table/Local.phpt | 79 +++++++++ tests/Symbol/Table/Shared.phpt | 164 ++++++++++++++++++ .../Symbol/{ => Table}/Shared/roundtrip.phpt | 0 6 files changed, 312 insertions(+), 25 deletions(-) create mode 100644 tests/Symbol/Table/Local.phpt create mode 100644 tests/Symbol/Table/Shared.phpt rename tests/Symbol/{ => Table}/Shared/roundtrip.phpt (100%) diff --git a/ion.c b/ion.c index 53b65df..762ea91 100644 --- a/ion.c +++ b/ion.c @@ -325,26 +325,7 @@ static ZEND_METHOD(ion_Catalog, add) 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)); @@ -364,12 +345,22 @@ static ZEND_METHOD(ion_Catalog, remove) 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)); + RETVAL_BOOL(SUCCESS == zend_hash_index_del(Z_ARRVAL_P(ztabs), idx)); + ION_CHECK(ion_catalog_release_symbol_table(obj->cat, php_ion_obj(symbol_table, zo_symtab)->tab)); } 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); + bool deleted = false; + ION_SYMBOL_TABLE *tab; + ION_STRING is; + ion_string_from_zend(&is, zs_symtab); + do { + tab = NULL; + ION_CHECK(ion_catalog_find_best_match(obj->cat, &is, 0, &tab)); + if (tab) { + ION_CHECK(ion_catalog_release_symbol_table(obj->cat, tab)); + deleted = true; + } + } while(tab); + RETVAL_BOOL(deleted); } } } diff --git a/ion_private.h b/ion_private.h index 3f13897..1bc1c15 100644 --- a/ion_private.h +++ b/ion_private.h @@ -1222,13 +1222,56 @@ static inline iERR php_ion_writer_buffer_handler(struct _ion_user_stream *user) return IERR_OK; } +static inline ION_COLLECTION *php_ion_catalog_collection(php_ion_catalog *cat) +{ + /* do not look too close */ + struct { + void *owner; + ION_SYMBOL_TABLE *sys; + ION_COLLECTION collection; + } *cat_ptr = (void *) cat->cat; + return &cat_ptr->collection; +} + +static inline void php_ion_writer_options_init_shared_imports(php_ion_writer_options *opt) +{ + php_ion_catalog *cat = php_ion_obj(catalog, opt->cat); + OBJ_CHECK(cat); + + ION_CHECK(ion_writer_options_initialize_shared_imports(&opt->opt)); + + ION_COLLECTION *col = php_ion_catalog_collection(cat); + if (!ION_COLLECTION_IS_EMPTY(col)) { + // holy, nah, forget it batman... +#ifndef IPCN_pNODE_TO_pDATA +# define IPCN_pNODE_TO_pDATA(x) (&((x)->_data[0])) +#endif + ION_COLLECTION_CURSOR cur; + ION_COLLECTION_OPEN(col, cur); + while (cur) { + ION_SYMBOL_TABLE **ptr; + ION_COLLECTION_NEXT(cur, ptr); + if (*ptr) { + ION_CHECK(ion_writer_options_add_shared_imports_symbol_tables(&opt->opt, ptr, 1)); + } else { + break; + } + } + } +} + static inline void php_ion_writer_ctor(php_ion_writer *obj) { + php_ion_writer_options *opt = NULL; + if (obj->opt) { update_property_obj(&obj->std, ZEND_STRL("options"), obj->opt); + opt = php_ion_obj(writer_options, obj->opt); + if (opt->cat) { + php_ion_writer_options_init_shared_imports(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; @@ -1238,6 +1281,7 @@ static inline void php_ion_writer_ctor(php_ion_writer *obj) php_ion_writer_buffer_init(obj); } + ION_CHECK(ion_writer_open_stream(&obj->writer, h, obj, opt ? &opt->opt : NULL)); OBJ_CHECK(obj); } @@ -1247,6 +1291,12 @@ static inline void php_ion_writer_dtor(php_ion_writer *obj) if (obj->writer) { ion_writer_close(obj->writer); } + if (obj->opt) { + php_ion_writer_options *opt = php_ion_obj(writer_options, obj->opt); + if (opt->cat) { + ion_writer_options_close_shared_imports(&opt->opt); + } + } if (obj->type == STREAM_WRITER) { if (obj->stream.buf.value) { efree(obj->stream.buf.value); diff --git a/tests/Catalog.phpt b/tests/Catalog.phpt index 133f161..d5ba180 100644 --- a/tests/Catalog.phpt +++ b/tests/Catalog.phpt @@ -20,6 +20,8 @@ var_dump($c); var_dump($c->find("shared", 1)); var_dump($c->findBest("shared")); +$c->remove("shared"); +var_dump(count($c)); ?> DONE --EXPECTF-- @@ -111,4 +113,5 @@ object(ion\Symbol\Table\Shared)#%d (3) { array(0) { } } +int(1) DONE diff --git a/tests/Symbol/Table/Local.phpt b/tests/Symbol/Table/Local.phpt new file mode 100644 index 0000000..5dc7778 --- /dev/null +++ b/tests/Symbol/Table/Local.phpt @@ -0,0 +1,79 @@ +--TEST-- +ion\Symbol\Table\Local +--EXTENSIONS-- +ion +--FILE-- +TEST +writeSymbol("local1"); +$w->writeSymbol("local1"); +$w->writeSymbol("local2"); +$w->writeSymbol("local2"); + +$w->finish(); + +foreach (str_split($buf, 8) as $line) { + printf("%-26s", chunk_split(bin2hex($line), 2, " ")); + foreach (str_split($line) as $byte) { + echo ctype_print($byte) ? $byte : "."; + } + echo "\n"; +} +echo "\n"; + +$u = new ion\Unserializer\PHP(multiSequence: true); +var_dump($u($buf)); + +?> +DONE +--EXPECTF-- +TEST +e0 01 00 ea ee 95 81 83 ........ +de 91 87 be 8e 86 6c 6f ......lo +63 61 6c 31 86 6c 6f 63 cal1.loc +61 6c 32 71 0a 71 0a 71 al2q.q.q +0b 71 0b .q. + +array(4) { + [0]=> + object(ion\Symbol)#%d (3) { + ["value"]=> + string(6) "local1" + ["sid"]=> + int(10) + ["importLocation"]=> + NULL + } + [1]=> + object(ion\Symbol)#%d (3) { + ["value"]=> + string(6) "local1" + ["sid"]=> + int(10) + ["importLocation"]=> + NULL + } + [2]=> + object(ion\Symbol)#%d (3) { + ["value"]=> + string(6) "local2" + ["sid"]=> + int(11) + ["importLocation"]=> + NULL + } + [3]=> + object(ion\Symbol)#%d (3) { + ["value"]=> + string(6) "local2" + ["sid"]=> + int(11) + ["importLocation"]=> + NULL + } +} +DONE diff --git a/tests/Symbol/Table/Shared.phpt b/tests/Symbol/Table/Shared.phpt new file mode 100644 index 0000000..2d51c48 --- /dev/null +++ b/tests/Symbol/Table/Shared.phpt @@ -0,0 +1,164 @@ +--TEST-- +ion\Symbol\Table\Shared +--EXTENSIONS-- +ion +--FILE-- +TEST +add(new ion\Symbol\Table\Shared("shared", 1, ["shared1", "shared2"])); +$w = new ion\Writer\Buffer\Writer($buf, + new ion\Writer\Options(catalog: $c, outputBinary: true)); + +$w->writeSymbol("shared1"); +$w->writeSymbol("shared1"); +$w->writeSymbol("shared2"); +$w->writeSymbol("shared2"); + +$w->finish(); + +foreach (str_split($buf, 8) as $line) { + printf("%-26s", chunk_split(bin2hex($line), 2, " ")); + foreach (str_split($line) as $byte) { + echo ctype_print($byte) ? $byte : "."; + } + echo "\n"; +} +echo "\n"; + +$u = new ion\Unserializer\PHP(multiSequence: true); +var_dump($u($buf)); + +$u = new ion\Unserializer\PHP(multiSequence: true, + readerOptions: new ion\Reader\Options(catalog: $c)); +var_dump($u($buf)); + +?> +DONE +--EXPECTF-- +TEST +e0 01 00 ea ee 97 81 83 ........ +de 93 86 be 90 de 8e 84 ........ +86 73 68 61 72 65 64 85 .shared. +21 01 88 21 02 71 0a 71 !..!.q.q +0a 71 0b 71 0b .q.q. + +array(4) { + [0]=> + object(ion\Symbol)#%d (3) { + ["value"]=> + string(0) "" + ["sid"]=> + int(10) + ["importLocation"]=> + object(ion\Symbol\ImportLocation)#%d (2) { + ["name"]=> + string(6) "shared" + ["location"]=> + int(1) + } + } + [1]=> + object(ion\Symbol)#%d (3) { + ["value"]=> + string(0) "" + ["sid"]=> + int(10) + ["importLocation"]=> + object(ion\Symbol\ImportLocation)#%d (2) { + ["name"]=> + string(6) "shared" + ["location"]=> + int(1) + } + } + [2]=> + object(ion\Symbol)#%d (3) { + ["value"]=> + string(0) "" + ["sid"]=> + int(11) + ["importLocation"]=> + object(ion\Symbol\ImportLocation)#%d (2) { + ["name"]=> + string(6) "shared" + ["location"]=> + int(2) + } + } + [3]=> + object(ion\Symbol)#%d (3) { + ["value"]=> + string(0) "" + ["sid"]=> + int(11) + ["importLocation"]=> + object(ion\Symbol\ImportLocation)#%d (2) { + ["name"]=> + string(6) "shared" + ["location"]=> + int(2) + } + } +} +on_context_change +array(4) { + [0]=> + object(ion\Symbol)#%d (3) { + ["value"]=> + string(7) "shared1" + ["sid"]=> + int(10) + ["importLocation"]=> + object(ion\Symbol\ImportLocation)#%d (2) { + ["name"]=> + string(6) "shared" + ["location"]=> + int(1) + } + } + [1]=> + object(ion\Symbol)#%d (3) { + ["value"]=> + string(7) "shared1" + ["sid"]=> + int(10) + ["importLocation"]=> + object(ion\Symbol\ImportLocation)#%d (2) { + ["name"]=> + string(6) "shared" + ["location"]=> + int(1) + } + } + [2]=> + object(ion\Symbol)#%d (3) { + ["value"]=> + string(7) "shared2" + ["sid"]=> + int(11) + ["importLocation"]=> + object(ion\Symbol\ImportLocation)#%d (2) { + ["name"]=> + string(6) "shared" + ["location"]=> + int(2) + } + } + [3]=> + object(ion\Symbol)#%d (3) { + ["value"]=> + string(7) "shared2" + ["sid"]=> + int(11) + ["importLocation"]=> + object(ion\Symbol\ImportLocation)#%d (2) { + ["name"]=> + string(6) "shared" + ["location"]=> + int(2) + } + } +} +DONE diff --git a/tests/Symbol/Shared/roundtrip.phpt b/tests/Symbol/Table/Shared/roundtrip.phpt similarity index 100% rename from tests/Symbol/Shared/roundtrip.phpt rename to tests/Symbol/Table/Shared/roundtrip.phpt -- 2.30.2