Nikolay Sivov : msxml3/mxwriter: No need to use contiguous allocation in case of BSTR output.

Alexandre Julliard julliard at wine.codeweavers.com
Mon Mar 28 09:12:36 CDT 2016


Module: wine
Branch: master
Commit: 8147f9001d03258deb9929dd838b98fdff03ad94
URL:    http://source.winehq.org/git/wine.git/?a=commit;h=8147f9001d03258deb9929dd838b98fdff03ad94

Author: Nikolay Sivov <nsivov at codeweavers.com>
Date:   Sun Mar 27 17:23:00 2016 +0300

msxml3/mxwriter: No need to use contiguous allocation in case of BSTR output.

Signed-off-by: Nikolay Sivov <nsivov at codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard at winehq.org>

---

 dlls/msxml3/mxwriter.c | 114 +++++++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 106 insertions(+), 8 deletions(-)

diff --git a/dlls/msxml3/mxwriter.c b/dlls/msxml3/mxwriter.c
index cc6c0b5..d6f23e2 100644
--- a/dlls/msxml3/mxwriter.c
+++ b/dlls/msxml3/mxwriter.c
@@ -34,6 +34,7 @@
 #include "msxml6.h"
 
 #include "wine/debug.h"
+#include "wine/list.h"
 
 #include "msxml_private.h"
 
@@ -121,6 +122,7 @@ typedef enum
 
 typedef struct
 {
+    struct list entry;
     char *data;
     unsigned int allocated;
     unsigned int written;
@@ -131,6 +133,8 @@ typedef struct
     encoded_buffer utf16;
     encoded_buffer encoded;
     UINT code_page;
+    UINT utf16_total;   /* total number of bytes written since last buffer reinitialization */
+    struct list blocks; /* only used when output was not set, for BSTR case */
 } output_buffer;
 
 typedef struct
@@ -304,14 +308,25 @@ static HRESULT init_output_buffer(xml_encoding encoding, output_buffer *buffer)
     }
     else
         memset(&buffer->encoded, 0, sizeof(buffer->encoded));
+    list_init(&buffer->blocks);
+    buffer->utf16_total = 0;
 
     return S_OK;
 }
 
 static void free_output_buffer(output_buffer *buffer)
 {
+    encoded_buffer *cur, *cur2;
+
     free_encoded_buffer(&buffer->encoded);
     free_encoded_buffer(&buffer->utf16);
+
+    LIST_FOR_EACH_ENTRY_SAFE(cur, cur2, &buffer->blocks, encoded_buffer, entry)
+    {
+        list_remove(&cur->entry);
+        free_encoded_buffer(cur);
+        heap_free(cur);
+    }
 }
 
 static void grow_buffer(encoded_buffer *buffer, int length)
@@ -331,6 +346,58 @@ static HRESULT write_output_buffer_mode(mxwriter *writer, output_mode mode, cons
     int length;
     char *ptr;
 
+    /* When writer has no output set we have to accumulate everything to return it later in a form of BSTR.
+       To achieve that:
+
+       - fill a buffer already allocated as part of output buffer;
+       - when current buffer is full, allocate another one and switch to it; buffers themselves never grow,
+         but are linked together, with head pointing to first allocated buffer after initial one got filled;
+       - later during get_output() contents are concatenated by copying one after another to destination BSTR buffer,
+         that's returned to the client. */
+    if (!writer->dest && (mode & (OutputBuffer_Native | OutputBuffer_Both)))
+    {
+        encoded_buffer *buff;
+
+        /* select last used block */
+        if (list_empty(&buffer->blocks))
+            buff = &buffer->utf16;
+        else
+            buff = LIST_ENTRY(list_tail(&buffer->blocks), encoded_buffer, entry);
+
+        length = (len == -1 ? strlenW(data) : len) * sizeof(WCHAR);
+
+        while (length)
+        {
+            unsigned int avail = buff->allocated - buff->written;
+            unsigned int written = min(avail, length);
+
+            if (avail)
+            {
+                memcpy(buff->data + buff->written, data, written);
+                buff->written += written;
+                buffer->utf16_total += written;
+                length -= written;
+            }
+
+            /* alloc new block if needed and retry */
+            if (length)
+            {
+                encoded_buffer *next = heap_alloc(sizeof(*next));
+                HRESULT hr;
+
+                if (FAILED(hr = init_encoded_buffer(next))) {
+                    heap_free(next);
+                    return hr;
+                }
+
+                list_add_tail(&buffer->blocks, &next->entry);
+                buff = next;
+            }
+        }
+
+        return S_OK;
+    }
+
     if (mode & (OutputBuffer_Encoded | OutputBuffer_Both)) {
         if (buffer->code_page != ~0)
         {
@@ -378,13 +445,25 @@ static HRESULT write_output_buffer_quoted(mxwriter *writer, const WCHAR *data, i
 }
 
 /* frees buffer data, reallocates with a default lengths */
-static void close_output_buffer(mxwriter *This)
+static void close_output_buffer(mxwriter *writer)
 {
-    heap_free(This->buffer.utf16.data);
-    heap_free(This->buffer.encoded.data);
-    init_encoded_buffer(&This->buffer.utf16);
-    init_encoded_buffer(&This->buffer.encoded);
-    get_code_page(This->xml_enc, &This->buffer.code_page);
+    encoded_buffer *cur, *cur2;
+
+    heap_free(writer->buffer.utf16.data);
+    heap_free(writer->buffer.encoded.data);
+
+    LIST_FOR_EACH_ENTRY_SAFE(cur, cur2, &writer->buffer.blocks, encoded_buffer, entry)
+    {
+        list_remove(&cur->entry);
+        free_encoded_buffer(cur);
+        heap_free(cur);
+    }
+
+    init_encoded_buffer(&writer->buffer.utf16);
+    init_encoded_buffer(&writer->buffer.encoded);
+    get_code_page(writer->xml_enc, &writer->buffer.code_page);
+    writer->buffer.utf16_total = 0;
+    list_init(&writer->buffer.blocks);
 }
 
 /* Escapes special characters like:
@@ -867,12 +946,31 @@ static HRESULT WINAPI mxwriter_get_output(IMXWriter *iface, VARIANT *dest)
 
     if (!This->dest)
     {
-        HRESULT hr = flush_output_buffer(This);
+        encoded_buffer *buff;
+        char *dest_ptr;
+        HRESULT hr;
+
+        hr = flush_output_buffer(This);
         if (FAILED(hr))
             return hr;
 
         V_VT(dest)   = VT_BSTR;
-        V_BSTR(dest) = SysAllocString((WCHAR*)This->buffer.utf16.data);
+        V_BSTR(dest) = SysAllocStringLen(NULL, This->buffer.utf16_total / sizeof(WCHAR));
+
+        dest_ptr = (char*)V_BSTR(dest);
+        buff = &This->buffer.utf16;
+
+        if (buff->written)
+        {
+            memcpy(dest_ptr, buff->data, buff->written);
+            dest_ptr += buff->written;
+        }
+
+        LIST_FOR_EACH_ENTRY(buff, &This->buffer.blocks, encoded_buffer, entry)
+        {
+            memcpy(dest_ptr, buff->data, buff->written);
+            dest_ptr += buff->written;
+        }
 
         return S_OK;
     }




More information about the wine-cvs mailing list