From 737d90c21c8de535642dc6969c16662cb9c48202 Mon Sep 17 00:00:00 2001 From: Nikolay Sivov Date: Sun, 27 Mar 2016 17:23:02 +0300 Subject: [PATCH] msxml3/mxwriter: Flush internal buffer as soon as it's filled. Signed-off-by: Nikolay Sivov Signed-off-by: Alexandre Julliard --- dlls/msxml3/mxwriter.c | 228 ++++++++++++++++++++--------------------- 1 file changed, 109 insertions(+), 119 deletions(-) diff --git a/dlls/msxml3/mxwriter.c b/dlls/msxml3/mxwriter.c index 23a8fd17668..1840fa1e1a0 100644 --- a/dlls/msxml3/mxwriter.c +++ b/dlls/msxml3/mxwriter.c @@ -97,13 +97,6 @@ static const struct xml_encoding_data xml_encoding_map[] = { { utf8W, XmlEncoding_UTF8, CP_UTF8 } }; -typedef enum -{ - OutputBuffer_Native = 0x001, - OutputBuffer_Encoded = 0x010, - OutputBuffer_Both = 0x100 -} output_mode; - typedef enum { MXWriter_BOM = 0, @@ -130,7 +123,6 @@ typedef struct typedef struct { - encoded_buffer utf16; encoded_buffer encoded; UINT code_page; UINT utf16_total; /* total number of bytes written since last buffer reinitialization */ @@ -173,7 +165,6 @@ typedef struct BSTR element; IStream *dest; - ULONG dest_written; output_buffer buffer; } mxwriter; @@ -293,21 +284,10 @@ static HRESULT init_output_buffer(xml_encoding encoding, output_buffer *buffer) if (hr != S_OK) return hr; - hr = init_encoded_buffer(&buffer->utf16); + hr = init_encoded_buffer(&buffer->encoded); if (hr != S_OK) return hr; - /* currently we always create a default output buffer that is UTF-16 only, - but it's possible to allocate with specific encoding too */ - if (encoding != XmlEncoding_UTF16) { - hr = init_encoded_buffer(&buffer->encoded); - if (hr != S_OK) { - free_encoded_buffer(&buffer->utf16); - return hr; - } - } - else - memset(&buffer->encoded, 0, sizeof(buffer->encoded)); list_init(&buffer->blocks); buffer->utf16_total = 0; @@ -319,7 +299,6 @@ 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) { @@ -329,23 +308,93 @@ static void free_output_buffer(output_buffer *buffer) } } -static void grow_buffer(encoded_buffer *buffer, int length) -{ - /* grow if needed, plus 4 bytes to be sure null terminator will fit in */ - if (buffer->allocated < buffer->written + length + 4) - { - int grown_size = max(2*buffer->allocated, buffer->allocated + length); - buffer->data = heap_realloc(buffer->data, grown_size); - buffer->allocated = grown_size; - } -} - -static HRESULT write_output_buffer_mode(mxwriter *writer, output_mode mode, const WCHAR *data, int len) +static HRESULT write_output_buffer(mxwriter *writer, const WCHAR *data, int len) { output_buffer *buffer = &writer->buffer; - int length; - char *ptr; + encoded_buffer *buff; + unsigned int written; + int src_len; + if (!len || !*data) + return S_OK; + + src_len = len == -1 ? strlenW(data) : len; + if (writer->dest) + { + buff = &buffer->encoded; + + if (buffer->code_page == ~0) + { + unsigned int avail = buff->allocated - buff->written; + + src_len *= sizeof(WCHAR); + written = min(avail, src_len); + + /* fill internal buffer first */ + if (avail) + { + memcpy(buff->data + buff->written, data, written); + data += written / sizeof(WCHAR); + buff->written += written; + avail -= written; + src_len -= written; + } + + if (!avail) + { + IStream_Write(writer->dest, buff->data, buff->written, &written); + buff->written = 0; + if (src_len >= buff->allocated) + IStream_Write(writer->dest, data, src_len, &written); + else if (src_len) + { + memcpy(buff->data, data, src_len); + buff->written += src_len; + } + } + } + else + { + unsigned int avail = buff->allocated - buff->written; + int length; + + length = WideCharToMultiByte(buffer->code_page, 0, data, src_len, NULL, 0, NULL, NULL); + if (avail >= length) + { + length = WideCharToMultiByte(buffer->code_page, 0, data, src_len, buff->data + buff->written, length, NULL, NULL); + buff->written += length; + } + else + { + /* drain what we go so far */ + if (buff->written) + { + IStream_Write(writer->dest, buff->data, buff->written, &written); + buff->written = 0; + avail = buff->allocated; + } + + if (avail >= length) + { + length = WideCharToMultiByte(buffer->code_page, 0, data, src_len, buff->data + buff->written, length, NULL, NULL); + buff->written += length; + } + else + { + char *mb; + + /* if current chunk is larger than total buffer size, convert it at once using temporary allocated buffer */ + mb = heap_alloc(length); + if (!mb) + return E_OUTOFMEMORY; + + length = WideCharToMultiByte(buffer->code_page, 0, data, src_len, mb, length, NULL, NULL); + IStream_Write(writer->dest, mb, length, &written); + heap_free(mb); + } + } + } + } /* When writer has no output set we have to accumulate everything to return it later in a form of BSTR. To achieve that: @@ -354,33 +403,30 @@ static HRESULT write_output_buffer_mode(mxwriter *writer, output_mode mode, cons 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))) + else { - encoded_buffer *buff; - /* select last used block */ if (list_empty(&buffer->blocks)) - buff = &buffer->utf16; + buff = &buffer->encoded; else buff = LIST_ENTRY(list_tail(&buffer->blocks), encoded_buffer, entry); - length = (len == -1 ? strlenW(data) : len) * sizeof(WCHAR); - - while (length) + src_len *= sizeof(WCHAR); + while (src_len) { unsigned int avail = buff->allocated - buff->written; - unsigned int written = min(avail, length); + unsigned int written = min(avail, src_len); if (avail) { memcpy(buff->data + buff->written, data, written); buff->written += written; buffer->utf16_total += written; - length -= written; + src_len -= written; } /* alloc new block if needed and retry */ - if (length) + if (src_len) { encoded_buffer *next = heap_alloc(sizeof(*next)); HRESULT hr; @@ -394,47 +440,11 @@ static HRESULT write_output_buffer_mode(mxwriter *writer, output_mode mode, cons buff = next; } } - - return S_OK; - } - - if (mode & (OutputBuffer_Encoded | OutputBuffer_Both)) { - if (buffer->code_page != ~0) - { - length = WideCharToMultiByte(buffer->code_page, 0, data, len, NULL, 0, NULL, NULL); - grow_buffer(&buffer->encoded, length); - ptr = buffer->encoded.data + buffer->encoded.written; - length = WideCharToMultiByte(buffer->code_page, 0, data, len, ptr, length, NULL, NULL); - buffer->encoded.written += len == -1 ? length-1 : length; - } - } - - if (mode & (OutputBuffer_Native | OutputBuffer_Both)) { - /* WCHAR data just copied */ - length = len == -1 ? strlenW(data) : len; - if (length) - { - length *= sizeof(WCHAR); - - grow_buffer(&buffer->utf16, length); - ptr = buffer->utf16.data + buffer->utf16.written; - - memcpy(ptr, data, length); - buffer->utf16.written += length; - ptr += length; - /* null termination */ - memset(ptr, 0, sizeof(WCHAR)); - } } return S_OK; } -static HRESULT write_output_buffer(mxwriter *writer, const WCHAR *data, int len) -{ - return write_output_buffer_mode(writer, OutputBuffer_Both, data, len); -} - static HRESULT write_output_buffer_quoted(mxwriter *writer, const WCHAR *data, int len) { write_output_buffer(writer, quotW, 1); @@ -449,7 +459,6 @@ static void close_output_buffer(mxwriter *writer) { 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) @@ -459,7 +468,6 @@ static void close_output_buffer(mxwriter *writer) 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; @@ -553,9 +561,10 @@ static void write_prolog_buffer(mxwriter *writer) /* encoding */ write_output_buffer(writer, encodingW, sizeof(encodingW)/sizeof(WCHAR)); - /* always write UTF-16 to WCHAR buffer */ - write_output_buffer_mode(writer, OutputBuffer_Native, utf16W, sizeof(utf16W)/sizeof(WCHAR) - 1); - write_output_buffer_mode(writer, OutputBuffer_Encoded, writer->encoding, -1); + if (writer->dest) + write_output_buffer(writer, writer->encoding, -1); + else + write_output_buffer(writer, utf16W, sizeof(utf16W)/sizeof(WCHAR) - 1); write_output_buffer(writer, quotW, 1); /* standalone */ @@ -572,43 +581,26 @@ static void write_prolog_buffer(mxwriter *writer) /* Attempts to the write data from the mxwriter's buffer to * the destination stream (if there is one). */ -static HRESULT write_data_to_stream(mxwriter *This) +static HRESULT write_data_to_stream(mxwriter *writer) { - encoded_buffer *buffer; + encoded_buffer *buffer = &writer->buffer.encoded; ULONG written = 0; - HRESULT hr; - if (!This->dest) + if (!writer->dest) return S_OK; - if (This->xml_enc != XmlEncoding_UTF16) - buffer = &This->buffer.encoded; + if (buffer->written == 0) + { + if (writer->xml_enc == XmlEncoding_UTF8) + IStream_Write(writer->dest, buffer->data, 0, &written); + } else - buffer = &This->buffer.utf16; - - if (This->dest_written > buffer->written) { - ERR("Failed sanity check! Not sure what to do... (%d > %d)\n", This->dest_written, buffer->written); - return E_FAIL; - } else if (This->dest_written == buffer->written && This->xml_enc != XmlEncoding_UTF8) - /* Windows seems to make an empty write call when the encoding is UTF-8 and - * all the data has been written to the stream. It doesn't seem make this call - * for any other encodings. - */ - return S_OK; - - /* Write the current content from the output buffer into 'dest'. - * TODO: Check what Windows does if the IStream doesn't write all of - * the data we give it at once. - */ - hr = IStream_Write(This->dest, buffer->data+This->dest_written, - buffer->written-This->dest_written, &written); - if (FAILED(hr)) { - WARN("Failed to write data to IStream (0x%08x)\n", hr); - return hr; + { + IStream_Write(writer->dest, buffer->data, buffer->written, &written); + buffer->written = 0; } - This->dest_written += written; - return hr; + return S_OK; } /* Newly added element start tag left unclosed cause for empty elements @@ -678,7 +670,6 @@ static inline HRESULT flush_output_buffer(mxwriter *This) static inline void reset_output_buffer(mxwriter *This) { close_output_buffer(This); - This->dest_written = 0; } static HRESULT writer_set_property(mxwriter *writer, mxwriter_prop property, VARIANT_BOOL value) @@ -967,7 +958,7 @@ static HRESULT WINAPI mxwriter_get_output(IMXWriter *iface, VARIANT *dest) return E_OUTOFMEMORY; dest_ptr = (char*)V_BSTR(dest); - buff = &This->buffer.utf16; + buff = &This->buffer.encoded; if (buff->written) { @@ -2607,7 +2598,6 @@ HRESULT MXWriter_create(MSXML_VERSION version, void **ppObj) This->newline = FALSE; This->dest = NULL; - This->dest_written = 0; hr = init_output_buffer(This->xml_enc, &This->buffer); if (hr != S_OK) {