improve buffer writer
authorMichael Wallner <mike@php.net>
Wed, 12 Jan 2022 10:02:03 +0000 (11:02 +0100)
committerMichael Wallner <mike@php.net>
Wed, 12 Jan 2022 10:36:35 +0000 (11:36 +0100)
ion.c
ion_private.h
tests/Writer/Buffer.phpt

diff --git a/ion.c b/ion.c
index 40c7ea1944557b25d294d51a0ea0630730300213..28d5a5cdc3356a15198b7248e02361e5c83bec64 100644 (file)
--- a/ion.c
+++ b/ion.c
@@ -1624,8 +1624,7 @@ static ZEND_METHOD(ion_Writer_Buffer_Writer, getBuffer)
 
        ZEND_PARSE_PARAMETERS_NONE();
 
-       smart_str_0(&obj->buffer.str.s);
-       RETVAL_STR_COPY(obj->buffer.str.s);
+       RETVAL_STR(php_ion_writer_buffer_copy(obj));
 }
 static ZEND_METHOD(ion_Writer_Buffer_Writer, resetBuffer)
 {
index 1f40eb49bfb06cb75e73f7d9bbc9e37776595417..aea7c7de7ec97b54d6404441f128ef901ca3c472 100644 (file)
@@ -1364,15 +1364,20 @@ LOCAL void php_ion_writer_stream_init(php_ion_writer *obj, php_ion_writer_option
        obj->stream.buf.value = emalloc(obj->stream.buf.length);
 }
 
-LOCAL void php_ion_writer_buffer_init(php_ion_writer *obj)
+LOCAL void php_ion_writer_buffer_offer(php_ion_writer *obj)
 {
-       smart_str_alloc(&obj->buffer.str, 0, false);
        if (obj->buffer.usr) {
                obj->buffer.usr->curr = (BYTE *) &obj->buffer.str.s->val[obj->buffer.str.s->len];
                obj->buffer.usr->limit = obj->buffer.usr->curr + obj->buffer.str.a - obj->buffer.str.s->len;
        }
 }
 
+LOCAL void php_ion_writer_buffer_init(php_ion_writer *obj)
+{
+       smart_str_alloc(&obj->buffer.str, 0, false);
+       php_ion_writer_buffer_offer(obj);
+}
+
 LOCAL void php_ion_writer_buffer_reset(php_ion_writer *obj)
 {
        smart_str_free(&obj->buffer.str);
@@ -1380,34 +1385,50 @@ LOCAL void php_ion_writer_buffer_reset(php_ion_writer *obj)
        php_ion_writer_buffer_init(obj);
 }
 
+LOCAL zend_string *php_ion_writer_buffer_copy(php_ion_writer *obj)
+{
+       if (obj->buffer.usr) {
+               // ensure that brain-dead ion_stream interface calls us again
+               obj->buffer.usr->curr = NULL;
+               obj->buffer.usr->limit = NULL;
+       }
+       smart_str_0(&obj->buffer.str);
+       return zend_string_copy(obj->buffer.str.s);
+}
+
+LOCAL void php_ion_writer_buffer_separate(php_ion_writer *obj, bool grow)
+{
+       // see zend_string_separate and smart_str_erealloc
+       zend_string *old_str = obj->buffer.str.s;
+       zend_string *new_str = zend_string_alloc(obj->buffer.str.a << grow, false);
+       memcpy(new_str->val, old_str->val, new_str->len = old_str->len);
+       zend_string_release(old_str);
+       obj->buffer.str.s = new_str;
+}
+
 LOCAL void php_ion_writer_buffer_grow(php_ion_writer *obj)
 {
-       if (GC_REFCOUNT(obj->buffer.str.s) > 1) {
-               zend_string *keep = obj->buffer.str.s;
-               obj->buffer.str.s = NULL;
-               smart_str_alloc(&obj->buffer.str, obj->buffer.str.a << 1, 0);
-               memcpy(obj->buffer.str.s->val, keep->val, keep->len + 1);
-               obj->buffer.str.s->len = keep->len;
-               zend_string_release(keep);
-       } else {
-               smart_str_erealloc(&obj->buffer.str, obj->buffer.str.a << 1);
+       if (obj->buffer.usr && obj->buffer.usr->curr) {
+               obj->buffer.str.s->len = obj->buffer.usr->curr - (BYTE *) obj->buffer.str.s->val;
+       }
+       if (obj->buffer.usr && obj->buffer.usr->curr && obj->buffer.usr->curr == obj->buffer.usr->limit) {
+               if (UNEXPECTED(GC_REFCOUNT(obj->buffer.str.s) > 1)) {
+                       php_ion_writer_buffer_separate(obj, true);
+               } else {
+                       smart_str_erealloc(&obj->buffer.str, obj->buffer.str.a << 1);
+               }
+       } else if (UNEXPECTED(GC_REFCOUNT(obj->buffer.str.s) > 1)) {
+               php_ion_writer_buffer_separate(obj, false);
        }
+       php_ion_writer_buffer_offer(obj);
 }
 
+
 LOCAL iERR php_ion_writer_buffer_handler(struct _ion_user_stream *user)
 {
        php_ion_writer *writer = (php_ion_writer *) user->handler_state;
        writer->buffer.usr = user;
-
-       if (user->curr) {
-               writer->buffer.str.s->len = user->curr - (BYTE *) writer->buffer.str.s->val;
-               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;
-       
+       php_ion_writer_buffer_grow(writer);
        return IERR_OK;
 }
 
@@ -1428,8 +1449,6 @@ LOCAL void php_ion_writer_options_init_shared_imports(php_ion_writer_options *op
                        ION_COLLECTION_NEXT(cur, ptr);
                        if (*ptr) {
                                ION_CHECK(ion_writer_options_add_shared_imports_symbol_tables(&opt->opt, ptr, 1));
-                       } else {
-                               break;
                        }
                }
        }
index 4eebf315ea58e681bbf7411b74b9a7683ecde318..d977b50d692bb60aa02e47f065af33c5c4907580 100644 (file)
@@ -7,27 +7,37 @@ TEST
 <?php
 
 $w = new ion\Writer\Buffer\Writer;
+for ($i = 0; $i < 10; ++$i)
+       $w->writeTypedNull(ion\Type::Int);
+$w->flush();
+var_dump($w->getBuffer());
+$ref=$w->getBuffer();
+// realloc
 for ($i = 0; $i < 100; ++$i)
        $w->writeTypedNull(ion\Type::Int);
 $w->flush();
-echo $w->getBuffer(),"\n";
+var_dump($ref, $w->getBuffer());
 $w->resetBuffer();
 var_dump($w->getBuffer());
 $w->writeSymbol("bar");
 $w->flush();
 var_dump($w->getBuffer());
+
 $w->resetBuffer();
+$ref=$w->getBuffer();
 // realloc
 for ($i = 0; $i < 100; ++$i)
        $w->writeTypedNull(ion\Type::Int);
 $w->flush();
-echo $w->getBuffer(),"\n";
+echo $ref,"----",$w->getBuffer(),"\n";
 ?>
 DONE
 --EXPECTF--
 TEST
-null.int%r( null.int)*%r
+string(89) "null.int%r( null.int){9}%r"
+string(89) "null.int%r( null.int){9}%r"
+string(989) "null.int%r( null.int){109}%r"
 string(0) ""
 string(4) " bar"
- null.int%r( null.int)*%r
+---- null.int%r( null.int){99}%r
 DONE