diff --git a/dlls/webservices/reader.c b/dlls/webservices/reader.c index 9a9d4917303..270aecc2201 100644 --- a/dlls/webservices/reader.c +++ b/dlls/webservices/reader.c @@ -6921,7 +6921,6 @@ static void set_input_buffer( struct reader *reader, const unsigned char *data, reader->text_conv_offset = 0; } -#define STREAM_BUFSIZE 4096 static void set_input_stream( struct reader *reader, WS_READ_CALLBACK callback, void *state ) { reader->input_type = WS_XML_READER_INPUT_TYPE_STREAM; diff --git a/dlls/webservices/tests/writer.c b/dlls/webservices/tests/writer.c index a55da605e84..8d189fd9e4e 100644 --- a/dlls/webservices/tests/writer.c +++ b/dlls/webservices/tests/writer.c @@ -4685,6 +4685,76 @@ static void test_repeating_element_choice(void) WsFreeWriter( writer ); } +static const struct stream_test +{ + ULONG min_size; + ULONG ret_size; +} +stream_tests[] = +{ + { 0, 4 }, + { 1, 4 }, + { 4, 4 }, + { 5, 4 }, +}; + +static CALLBACK HRESULT write_callback( void *state, const WS_BYTES *buf, ULONG count, + const WS_ASYNC_CONTEXT *ctx, WS_ERROR *error ) +{ + ULONG i = *(ULONG *)state; + ok( buf->length == stream_tests[i].ret_size, "%u: got %u\n", i, buf->length ); + ok( !memcmp( buf->bytes, "", stream_tests[i].ret_size ), "%u: wrong data\n", i ); + ok( count == 1, "%u: got %u\n", i, count ); + return S_OK; +} + +static void test_stream_output(void) +{ + static WS_XML_STRING str_ns = {0, NULL}, str_t = {1, (BYTE *)"t"}; + WS_XML_WRITER_TEXT_ENCODING text = {{WS_XML_WRITER_ENCODING_TYPE_TEXT}, WS_CHARSET_UTF8}; + WS_XML_WRITER_STREAM_OUTPUT stream; + WS_XML_WRITER *writer; + HRESULT hr; + ULONG i = 0; + + hr = WsCreateWriter( NULL, 0, &writer, NULL ); + ok( hr == S_OK, "got %08x\n", hr ); + + hr = WsFlushWriter( writer, 0, NULL, NULL ); + ok( hr == WS_E_INVALID_OPERATION, "got %08x\n", hr ); + + stream.output.outputType = WS_XML_WRITER_OUTPUT_TYPE_STREAM; + stream.writeCallback = write_callback; + stream.writeCallbackState = &i; + hr = WsSetOutput( writer, &text.encoding, &stream.output, NULL, 0, NULL ); + ok( hr == S_OK, "got %08x\n", hr ); + + hr = WsSetOutput( writer, &text.encoding, &stream.output, NULL, 0, NULL ); + ok( hr == S_OK, "got %08x\n", hr ); + + hr = WsWriteStartElement( writer, NULL, &str_t, &str_ns, NULL ); + ok( hr == S_OK, "got %08x\n", hr ); + hr = WsWriteEndElement( writer, NULL ); + ok( hr == S_OK, "got %08x\n", hr ); + hr = WsFlushWriter( writer, 0, NULL, NULL ); + ok( hr == S_OK, "got %08x\n", hr ); + + for (i = 0; i < ARRAY_SIZE(stream_tests); i++) + { + stream.writeCallbackState = &i; + hr = WsSetOutput( writer, &text.encoding, &stream.output, NULL, 0, NULL ); + ok( hr == S_OK, "%u: got %08x\n", i, hr ); + hr = WsWriteStartElement( writer, NULL, &str_t, &str_ns, NULL ); + ok( hr == S_OK, "%u: got %08x\n", i, hr ); + hr = WsWriteEndElement( writer, NULL ); + ok( hr == S_OK, "%u: got %08x\n", i, hr ); + hr = WsFlushWriter( writer, stream_tests[i].min_size, NULL, NULL ); + ok( hr == S_OK, "%u: got %08x\n", i, hr ); + } + + WsFreeWriter( writer ); +} + START_TEST(writer) { test_WsCreateWriter(); @@ -4728,4 +4798,5 @@ START_TEST(writer) test_union_type(); test_text_types_binary(); test_repeating_element_choice(); + test_stream_output(); } diff --git a/dlls/webservices/webservices.spec b/dlls/webservices/webservices.spec index adad68dbdd2..bd152b1ac34 100644 --- a/dlls/webservices/webservices.spec +++ b/dlls/webservices/webservices.spec @@ -47,7 +47,7 @@ @ stdcall WsFillReader(ptr long ptr ptr) @ stdcall WsFindAttribute(ptr ptr ptr long ptr ptr) @ stub WsFlushBody -@ stub WsFlushWriter +@ stdcall WsFlushWriter(ptr long ptr ptr) @ stdcall WsFreeChannel(ptr) @ stdcall WsFreeError(ptr) @ stdcall WsFreeHeap(ptr) diff --git a/dlls/webservices/webservices_private.h b/dlls/webservices/webservices_private.h index b482b5ba203..6a6f65dbec3 100644 --- a/dlls/webservices/webservices_private.h +++ b/dlls/webservices/webservices_private.h @@ -18,6 +18,8 @@ #include "winhttp.h" +#define STREAM_BUFSIZE 4096 + struct xmlbuf { WS_HEAP *heap; diff --git a/dlls/webservices/writer.c b/dlls/webservices/writer.c index 0a28d9dc84f..4f6cb601959 100644 --- a/dlls/webservices/writer.c +++ b/dlls/webservices/writer.c @@ -84,9 +84,12 @@ struct writer WS_XML_WRITER_ENCODING_TYPE output_enc; WS_CHARSET output_charset; WS_XML_WRITER_OUTPUT_TYPE output_type; + WS_WRITE_CALLBACK output_cb; + void *output_cb_state; struct xmlbuf *output_buf; BOOL output_buf_user; WS_HEAP *output_heap; + unsigned char *stream_buf; const WS_XML_DICTIONARY *dict; BOOL dict_do_lookup; WS_DYNAMIC_STRING_CALLBACK dict_cb; @@ -121,6 +124,7 @@ static void free_writer( struct writer *writer ) destroy_nodes( writer->root ); free_xml_string( writer->current_ns ); WsFreeHeap( writer->output_heap ); + heap_free( writer->stream_buf ); #ifndef __MINGW32__ writer->cs.DebugInfo->Spare[0] = 0; @@ -294,7 +298,7 @@ HRESULT WINAPI WsGetWriterProperty( WS_XML_WRITER *handle, WS_XML_WRITER_PROPERT return E_INVALIDARG; } - if (!writer->output_type) hr = WS_E_INVALID_OPERATION; + if (writer->output_type != WS_XML_WRITER_OUTPUT_TYPE_BUFFER) hr = WS_E_INVALID_OPERATION; else { switch (id) @@ -346,6 +350,15 @@ static void set_output_buffer( struct writer *writer, struct xmlbuf *xmlbuf ) writer->write_pos = 0; } +static void set_output_stream( struct writer *writer, WS_WRITE_CALLBACK callback, void *state ) +{ + writer->output_type = WS_XML_WRITER_OUTPUT_TYPE_STREAM; + writer->output_cb = callback; + writer->output_cb_state = state; + writer->write_bufptr = writer->stream_buf; + writer->write_pos = 0; +} + /************************************************************************** * WsSetOutput [webservices.@] */ @@ -384,7 +397,7 @@ HRESULT WINAPI WsSetOutput( WS_XML_WRITER *handle, const WS_XML_WRITER_ENCODING { case WS_XML_WRITER_ENCODING_TYPE_TEXT: { - WS_XML_WRITER_TEXT_ENCODING *text = (WS_XML_WRITER_TEXT_ENCODING *)encoding; + const WS_XML_WRITER_TEXT_ENCODING *text = (const WS_XML_WRITER_TEXT_ENCODING *)encoding; if (text->charSet != WS_CHARSET_UTF8) { FIXME( "charset %u not supported\n", text->charSet ); @@ -397,7 +410,7 @@ HRESULT WINAPI WsSetOutput( WS_XML_WRITER *handle, const WS_XML_WRITER_ENCODING } case WS_XML_WRITER_ENCODING_TYPE_BINARY: { - WS_XML_WRITER_BINARY_ENCODING *bin = (WS_XML_WRITER_BINARY_ENCODING *)encoding; + const WS_XML_WRITER_BINARY_ENCODING *bin = (const WS_XML_WRITER_BINARY_ENCODING *)encoding; writer->output_enc = WS_XML_WRITER_ENCODING_TYPE_BINARY; writer->output_charset = 0; writer->dict = bin->staticDictionary; @@ -426,6 +439,18 @@ HRESULT WINAPI WsSetOutput( WS_XML_WRITER *handle, const WS_XML_WRITER_ENCODING writer->output_buf_user = FALSE; break; } + case WS_XML_WRITER_OUTPUT_TYPE_STREAM: + { + const WS_XML_WRITER_STREAM_OUTPUT *stream = (const WS_XML_WRITER_STREAM_OUTPUT *)output; + if (!writer->stream_buf && !(writer->stream_buf = heap_alloc( STREAM_BUFSIZE ))) + { + hr = E_OUTOFMEMORY; + goto done; + } + set_output_stream( writer, stream->writeCallback, stream->writeCallbackState ); + break; + + } default: FIXME( "output type %u not supported\n", output->outputType ); hr = E_NOTIMPL; @@ -489,12 +514,63 @@ done: return hr; } +static HRESULT flush_writer( struct writer *writer, ULONG min_size, const WS_ASYNC_CONTEXT *ctx, + WS_ERROR *error ) +{ + WS_BYTES buf; + + if (writer->write_pos < min_size) return S_OK; + + buf.bytes = writer->write_bufptr; + buf.length = writer->write_pos; + writer->output_cb( writer->output_cb_state, &buf, 1, ctx, error ); + writer->write_pos = 0; + return S_OK; +} + +/************************************************************************** + * WsFlushWriter [webservices.@] + */ +HRESULT WINAPI WsFlushWriter( WS_XML_WRITER *handle, ULONG min_size, const WS_ASYNC_CONTEXT *ctx, + WS_ERROR *error ) +{ + struct writer *writer = (struct writer *)handle; + HRESULT hr; + + TRACE( "%p %u %p %p\n", handle, min_size, ctx, error ); + if (error) FIXME( "ignoring error parameter\n" ); + if (ctx) FIXME( "ignoring ctx parameter\n" ); + + if (!writer) return E_INVALIDARG; + + EnterCriticalSection( &writer->cs ); + + if (writer->magic != WRITER_MAGIC) + { + LeaveCriticalSection( &writer->cs ); + return E_INVALIDARG; + } + + if (writer->output_type != WS_XML_WRITER_OUTPUT_TYPE_STREAM) hr = WS_E_INVALID_OPERATION; + else hr = flush_writer( writer, min_size, ctx, error ); + + LeaveCriticalSection( &writer->cs ); + TRACE( "returning %08x\n", hr ); + return hr; +} + static HRESULT write_grow_buffer( struct writer *writer, ULONG size ) { struct xmlbuf *buf = writer->output_buf; SIZE_T new_size; void *tmp; + if (writer->output_type == WS_XML_WRITER_OUTPUT_TYPE_STREAM) + { + if (size > STREAM_BUFSIZE) return WS_E_QUOTA_EXCEEDED; + return flush_writer( writer, STREAM_BUFSIZE - size, NULL, NULL ); + } + if (buf->size >= writer->write_pos + size) { buf->bytes.length = writer->write_pos + size; @@ -2047,7 +2123,7 @@ HRESULT WINAPI WsWriteStartAttribute( WS_XML_WRITER *handle, const WS_XML_STRING } /* flush current start element if necessary */ -static HRESULT write_flush( struct writer *writer ) +static HRESULT write_commit( struct writer *writer ) { if (writer->state == WRITER_STATE_STARTELEMENT) { @@ -2089,7 +2165,7 @@ static HRESULT write_cdata( struct writer *writer ) static HRESULT write_cdata_node( struct writer *writer ) { HRESULT hr; - if ((hr = write_flush( writer )) != S_OK) return hr; + if ((hr = write_commit( writer )) != S_OK) return hr; if ((hr = write_add_cdata_node( writer )) != S_OK) return hr; if ((hr = write_add_endcdata_node( writer )) != S_OK) return hr; if ((hr = write_cdata( writer )) != S_OK) return hr; @@ -2220,7 +2296,7 @@ static HRESULT write_element_node( struct writer *writer, const WS_XML_STRING *p const WS_XML_STRING *localname, const WS_XML_STRING *ns ) { HRESULT hr; - if ((hr = write_flush( writer )) != S_OK) return hr; + if ((hr = write_commit( writer )) != S_OK) return hr; if ((hr = write_add_element_node( writer, prefix, localname, ns )) != S_OK) return hr; if ((hr = write_add_endelement_node( writer, writer->current )) != S_OK) return hr; writer->state = WRITER_STATE_STARTELEMENT; @@ -2826,7 +2902,7 @@ static HRESULT write_text_node( struct writer *writer, const WS_XML_TEXT *text ) ULONG offset = 0; HRESULT hr; - if ((hr = write_flush( writer )) != S_OK) return hr; + if ((hr = write_commit( writer )) != S_OK) return hr; if (node_type( writer->current ) != WS_XML_NODE_TYPE_TEXT) { if ((hr = write_add_text_node( writer, text )) != S_OK) return hr; @@ -4158,7 +4234,7 @@ HRESULT WINAPI WsWriteXmlBuffer( WS_XML_WRITER *handle, WS_XML_BUFFER *buffer, W goto done; } - if ((hr = write_flush( writer )) != S_OK) goto done; + if ((hr = write_commit( writer )) != S_OK) goto done; if ((hr = write_grow_buffer( writer, xmlbuf->bytes.length )) != S_OK) goto done; write_bytes( writer, xmlbuf->bytes.bytes, xmlbuf->bytes.length ); @@ -4261,7 +4337,7 @@ static HRESULT write_qualified_name( struct writer *writer, const WS_XML_STRING WS_XML_QNAME_TEXT qname = {{WS_XML_TEXT_TYPE_QNAME}}; HRESULT hr; - if ((hr = write_flush( writer )) != S_OK) return hr; + if ((hr = write_commit( writer )) != S_OK) return hr; if (!prefix && ((hr = find_prefix( writer, ns, &prefix )) != S_OK)) return hr; qname.prefix = (WS_XML_STRING *)prefix; @@ -4398,7 +4474,7 @@ HRESULT WINAPI WsMoveWriter( WS_XML_WRITER *handle, WS_MOVE_TO move, BOOL *found return E_INVALIDARG; } - if (!writer->output_type) hr = WS_E_INVALID_OPERATION; + if (writer->output_type != WS_XML_WRITER_OUTPUT_TYPE_BUFFER) hr = WS_E_INVALID_OPERATION; else hr = write_move_to( writer, move, found ); LeaveCriticalSection( &writer->cs ); @@ -4526,7 +4602,7 @@ static HRESULT write_comment( struct writer *writer ) static HRESULT write_comment_node( struct writer *writer, const WS_XML_STRING *value ) { HRESULT hr; - if ((hr = write_flush( writer )) != S_OK) return hr; + if ((hr = write_commit( writer )) != S_OK) return hr; if ((hr = write_add_comment_node( writer, value )) != S_OK) return hr; if ((hr = write_comment( writer )) != S_OK) return hr; writer->state = WRITER_STATE_COMMENT; diff --git a/include/webservices.h b/include/webservices.h index 69e3c8a2767..a6018d5d258 100644 --- a/include/webservices.h +++ b/include/webservices.h @@ -296,15 +296,21 @@ typedef struct _WS_ASYNC_CONTEXT { typedef HRESULT (CALLBACK *WS_READ_CALLBACK) (void*, void*, ULONG, ULONG*, const WS_ASYNC_CONTEXT*, WS_ERROR*); -typedef HRESULT (CALLBACK *WS_WRITE_CALLBACK) - (void*, const WS_BYTES*, ULONG, const WS_ASYNC_CONTEXT*, WS_ERROR*); - typedef struct _WS_XML_READER_STREAM_INPUT { WS_XML_READER_INPUT input; WS_READ_CALLBACK readCallback; void *readCallbackState; } WS_XML_READER_STREAM_INPUT; +typedef HRESULT (CALLBACK *WS_WRITE_CALLBACK) + (void*, const WS_BYTES*, ULONG, const WS_ASYNC_CONTEXT*, WS_ERROR*); + +typedef struct _WS_XML_WRITER_STREAM_OUTPUT { + WS_XML_WRITER_OUTPUT output; + WS_WRITE_CALLBACK writeCallback; + void *writeCallbackState; +} WS_XML_WRITER_STREAM_OUTPUT; + typedef enum { WS_ELEMENT_TYPE_MAPPING = 1, WS_ATTRIBUTE_TYPE_MAPPING = 2, @@ -1626,6 +1632,8 @@ HRESULT WINAPI WsFillBody(WS_MESSAGE*, ULONG, const WS_ASYNC_CONTEXT*, WS_ERROR* HRESULT WINAPI WsFillReader(WS_XML_READER*, ULONG, const WS_ASYNC_CONTEXT*, WS_ERROR*); HRESULT WINAPI WsFindAttribute(WS_XML_READER*, const WS_XML_STRING*, const WS_XML_STRING*, BOOL, ULONG*, WS_ERROR*); +HRESULT WINAPI WsFlushBody(WS_MESSAGE*, ULONG, const WS_ASYNC_CONTEXT*, WS_ERROR*); +HRESULT WINAPI WsFlushWriter(WS_XML_WRITER*, ULONG, const WS_ASYNC_CONTEXT*, WS_ERROR*); void WINAPI WsFreeChannel(WS_CHANNEL*); void WINAPI WsFreeError(WS_ERROR*); void WINAPI WsFreeHeap(WS_HEAP*);