Signed-off-by: Hans Leidekker hans@codeweavers.com --- dlls/webservices/reader.c | 202 ++++++++++++++++++++++++++++------------ dlls/webservices/tests/reader.c | 106 +++++++++++++++++++++ 2 files changed, 249 insertions(+), 59 deletions(-)
diff --git a/dlls/webservices/reader.c b/dlls/webservices/reader.c index cbce1f6afb..ba6a951e9c 100644 --- a/dlls/webservices/reader.c +++ b/dlls/webservices/reader.c @@ -390,10 +390,13 @@ struct reader WS_XML_READER_ENCODING_TYPE input_enc; WS_CHARSET input_charset; WS_XML_READER_INPUT_TYPE input_type; + WS_READ_CALLBACK input_cb; + void *input_cb_state; struct xmlbuf *input_buf; const unsigned char *input_data; ULONG input_size; ULONG text_conv_offset; + unsigned char *stream_buf; const WS_XML_DICTIONARY *dict_static; WS_XML_DICTIONARY *dict; ULONG prop_count; @@ -515,6 +518,7 @@ static void free_reader( struct reader *reader ) destroy_nodes( reader->root ); clear_prefixes( reader->prefixes, reader->nb_prefixes ); heap_free( reader->prefixes ); + heap_free( reader->stream_buf );
#ifndef __MINGW32__ reader->cs.DebugInfo->Spare[0] = 0; @@ -615,6 +619,29 @@ void WINAPI WsFreeReader( WS_XML_READER *handle ) free_reader( reader ); }
+static HRESULT read_more_data( struct reader *reader, ULONG min_size, const WS_ASYNC_CONTEXT *ctx, + WS_ERROR *error ) +{ + ULONG size = 0, max_size; + + if (reader->read_size - reader->read_pos >= min_size) return S_OK; + if (reader->input_type != WS_XML_READER_INPUT_TYPE_STREAM) return WS_E_INVALID_FORMAT; + if (min_size > reader->input_size) return WS_E_QUOTA_EXCEEDED; + + if (min_size > reader->input_size - reader->read_pos) + { + memmove( reader->stream_buf, reader->stream_buf + reader->read_pos, reader->read_size - reader->read_pos ); + reader->read_size -= reader->read_pos; + reader->read_pos = 0; + } + max_size = reader->input_size - reader->read_size; + + reader->input_cb( reader->input_cb_state, reader->stream_buf + reader->read_size, max_size, &size, ctx, error ); + if (size < min_size) return WS_E_QUOTA_EXCEEDED; + reader->read_size += size; + return S_OK; +} + /************************************************************************** * WsFillReader [webservices.@] */ @@ -622,7 +649,7 @@ HRESULT WINAPI WsFillReader( WS_XML_READER *handle, ULONG min_size, const WS_ASY WS_ERROR *error ) { struct reader *reader = (struct reader *)handle; - HRESULT hr = S_OK; + HRESULT hr;
TRACE( "%p %u %p %p\n", handle, min_size, ctx, error ); if (error) FIXME( "ignoring error parameter\n" ); @@ -637,9 +664,16 @@ HRESULT WINAPI WsFillReader( WS_XML_READER *handle, ULONG min_size, const WS_ASY return E_INVALIDARG; }
- /* FIXME: add support for stream input */ - reader->read_size = min( min_size, reader->input_size ); - reader->read_pos = 0; + if (reader->input_type == WS_XML_READER_INPUT_TYPE_STREAM) + { + hr = read_more_data( reader, min_size, ctx, error ); + } + else + { + reader->read_size = min( min_size, reader->input_size ); + reader->read_pos = 0; + hr = S_OK; + }
LeaveCriticalSection( &reader->cs ); TRACE( "returning %08x\n", hr ); @@ -931,7 +965,7 @@ WS_XML_DATETIME_TEXT *alloc_datetime_text( const WS_DATETIME *value )
static inline BOOL read_end_of_data( struct reader *reader ) { - return reader->read_pos >= reader->read_size; + return (read_more_data( reader, 1, NULL, NULL ) != S_OK); }
static inline const unsigned char *read_current_ptr( struct reader *reader ) @@ -939,25 +973,35 @@ static inline const unsigned char *read_current_ptr( struct reader *reader ) return &reader->read_bufptr[reader->read_pos]; }
+static inline void read_skip( struct reader *reader, unsigned int count ) +{ + assert( reader->read_pos + count <= reader->read_size ); + reader->read_pos += count; +} + static inline HRESULT read_peek( struct reader *reader, unsigned char *bytes, unsigned int len ) { - if (reader->read_pos + len > reader->read_size) return WS_E_INVALID_FORMAT; - memcpy( bytes, reader->read_bufptr + reader->read_pos, len ); + HRESULT hr; + if ((hr = read_more_data( reader, len, NULL, NULL )) != S_OK) return hr; + memcpy( bytes, read_current_ptr( reader ), len ); return S_OK; }
static inline HRESULT read_byte( struct reader *reader, unsigned char *byte ) { - if (reader->read_pos >= reader->read_size) return WS_E_INVALID_FORMAT; - *byte = reader->read_bufptr[reader->read_pos++]; + HRESULT hr; + if ((hr = read_more_data( reader, 1, NULL, NULL )) != S_OK) return hr; + *byte = *read_current_ptr( reader ); + read_skip( reader, 1 ); return S_OK; }
static inline HRESULT read_bytes( struct reader *reader, unsigned char *bytes, unsigned int len ) { - if (reader->read_pos + len > reader->read_size) return WS_E_INVALID_FORMAT; - memcpy( bytes, reader->read_bufptr + reader->read_pos, len ); - reader->read_pos += len; + HRESULT hr; + if ((hr = read_more_data( reader, len, NULL, NULL )) != S_OK) return hr; + memcpy( bytes, read_current_ptr( reader ), len ); + read_skip( reader, len ); return S_OK; }
@@ -985,20 +1029,22 @@ static const unsigned int utf8_minval[4] = { 0x0, 0x80, 0x800, 0x10000 }; static inline HRESULT read_utf8_char( struct reader *reader, unsigned int *ret, unsigned int *skip ) { unsigned int len; - unsigned char ch = reader->read_bufptr[reader->read_pos]; + unsigned char ch; const unsigned char *end; + HRESULT hr;
- if (reader->read_pos >= reader->read_size) return WS_E_INVALID_FORMAT; - + if ((hr = read_more_data( reader, 1, NULL, NULL )) != S_OK) return hr; + ch = *read_current_ptr( reader ); if (ch < 0x80) { *ret = ch; *skip = 1; return S_OK; } + len = utf8_length[ch - 0x80]; - if (reader->read_pos + len >= reader->read_size) return WS_E_INVALID_FORMAT; - end = reader->read_bufptr + reader->read_pos + len + 1; + if ((hr = read_more_data( reader, len, NULL, NULL )) != S_OK) return hr; + end = read_current_ptr( reader ) + len + 1; *ret = ch & utf8_mask[len];
switch (len) @@ -1020,12 +1066,6 @@ static inline HRESULT read_utf8_char( struct reader *reader, unsigned int *ret, return WS_E_INVALID_FORMAT; }
-static inline void read_skip( struct reader *reader, unsigned int count ) -{ - if (reader->read_pos + count > reader->read_size) return; - reader->read_pos += count; -} - static inline BOOL read_isnamechar( unsigned int ch ) { /* FIXME: incomplete */ @@ -1042,16 +1082,22 @@ static inline BOOL read_isspace( unsigned int ch )
static inline void read_skip_whitespace( struct reader *reader ) { - while (reader->read_pos < reader->read_size && read_isspace( reader->read_bufptr[reader->read_pos] )) - reader->read_pos++; + for (;;) + { + if (read_more_data( reader, 1, NULL, NULL ) != S_OK || !read_isspace( *read_current_ptr( reader ) )) break; + read_skip( reader, 1 ); + } }
static inline HRESULT read_cmp( struct reader *reader, const char *str, int len ) { - const unsigned char *ptr = read_current_ptr( reader ); + const unsigned char *ptr; + HRESULT hr;
if (len < 0) len = strlen( str ); - if (reader->read_pos + len > reader->read_size) return WS_E_INVALID_FORMAT; + if ((hr = read_more_data( reader, len, NULL, NULL )) != S_OK) return hr; + + ptr = read_current_ptr( reader ); while (len--) { if (*str != *ptr) return WS_E_INVALID_FORMAT; @@ -1064,9 +1110,8 @@ static HRESULT read_xmldecl( struct reader *reader ) { HRESULT hr;
- if (!reader->read_size) return WS_E_INVALID_FORMAT; - - if (read_cmp( reader, "<", 1 ) != S_OK || read_cmp( reader, "<?", 2 ) != S_OK) + if ((hr = read_more_data( reader, 1, NULL, NULL )) != S_OK) return hr; + if (*read_current_ptr( reader ) != '<' || (hr = read_cmp( reader, "<?", 2 ) != S_OK)) { reader->state = READER_STATE_BOF; return S_OK; @@ -1075,8 +1120,11 @@ static HRESULT read_xmldecl( struct reader *reader ) read_skip( reader, 6 );
/* FIXME: parse attributes */ - while (reader->read_pos < reader->read_size && reader->read_bufptr[reader->read_pos] != '?') - reader->read_pos++; + for (;;) + { + if (read_more_data( reader, 1, NULL, NULL ) != S_OK || *read_current_ptr( reader ) == '?' ) break; + read_skip( reader, 1 ); + }
if ((hr = read_cmp( reader, "?>", 2 )) != S_OK) return hr; read_skip( reader, 2 ); @@ -6829,19 +6877,15 @@ HRESULT WINAPI WsReadAttribute( WS_XML_READER *handle, const WS_ATTRIBUTE_DESCRI static inline BOOL is_utf8( const unsigned char *data, ULONG size, ULONG *offset ) { static const char bom[] = {0xef,0xbb,0xbf}; - const unsigned char *p = data; - - return (size >= sizeof(bom) && !memcmp( p, bom, sizeof(bom) ) && (*offset = sizeof(bom))) || + return (size >= sizeof(bom) && !memcmp( data, bom, sizeof(bom) ) && (*offset = sizeof(bom))) || (size > 2 && !(*offset = 0)); }
static inline BOOL is_utf16le( const unsigned char *data, ULONG size, ULONG *offset ) { static const char bom[] = {0xff,0xfe}; - const unsigned char *p = data; - - return (size >= sizeof(bom) && !memcmp( p, bom, sizeof(bom) ) && (*offset = sizeof(bom))) || - (size >= 4 && p[0] == '<' && !p[1] && !(*offset = 0)); + return (size >= sizeof(bom) && !memcmp( data, bom, sizeof(bom) ) && (*offset = sizeof(bom))) || + (size >= 4 && data[0] == '<' && !data[1] && !(*offset = 0)); }
static WS_CHARSET detect_charset( const unsigned char *data, ULONG size, ULONG *offset ) @@ -6862,10 +6906,10 @@ static WS_CHARSET detect_charset( const unsigned char *data, ULONG size, ULONG * return ret; }
-static void set_input_buffer( struct reader *reader, struct xmlbuf *buf, const unsigned char *data, ULONG size ) +static void set_input_buffer( struct reader *reader, const unsigned char *data, ULONG size ) { reader->input_type = WS_XML_READER_INPUT_TYPE_BUFFER; - reader->input_buf = buf; + reader->input_buf = NULL; reader->input_data = data; reader->input_size = size;
@@ -6876,6 +6920,28 @@ static void set_input_buffer( struct reader *reader, struct xmlbuf *buf, const u 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; + reader->input_cb = callback; + reader->input_cb_state = state; + reader->input_buf = NULL; + reader->input_data = reader->stream_buf; + reader->input_size = STREAM_BUFSIZE; + + if (reader->read_pos >= reader->read_size) reader->read_size = 0; + else + { + memmove( reader->stream_buf, reader->stream_buf + reader->read_pos, reader->read_size - reader->read_pos ); + reader->read_size -= reader->read_pos; + } + reader->read_pos = 0; + reader->read_bufptr = reader->input_data; + + reader->text_conv_offset = 0; +} + /************************************************************************** * WsSetInput [webservices.@] */ @@ -6914,19 +6980,14 @@ HRESULT WINAPI WsSetInput( WS_XML_READER *handle, const WS_XML_READER_ENCODING * { case WS_XML_READER_ENCODING_TYPE_TEXT: { - WS_XML_READER_TEXT_ENCODING *text = (WS_XML_READER_TEXT_ENCODING *)encoding; - WS_XML_READER_BUFFER_INPUT *buf = (WS_XML_READER_BUFFER_INPUT *)input; - - if (input->inputType != WS_XML_READER_INPUT_TYPE_BUFFER) + if (input->inputType == WS_XML_READER_INPUT_TYPE_BUFFER) { - FIXME( "charset detection on input type %u not supported\n", input->inputType ); - hr = E_NOTIMPL; - goto done; + WS_XML_READER_TEXT_ENCODING *text = (WS_XML_READER_TEXT_ENCODING *)encoding; + WS_XML_READER_BUFFER_INPUT *buf = (WS_XML_READER_BUFFER_INPUT *)input; + if (text->charSet != WS_CHARSET_AUTO) reader->input_charset = text->charSet; + else reader->input_charset = detect_charset( buf->encodedData, buf->encodedDataSize, &offset ); }
- if (text->charSet != WS_CHARSET_AUTO) reader->input_charset = text->charSet; - else reader->input_charset = detect_charset( buf->encodedData, buf->encodedDataSize, &offset ); - reader->input_enc = WS_XML_READER_ENCODING_TYPE_TEXT; break; } @@ -6950,8 +7011,18 @@ HRESULT WINAPI WsSetInput( WS_XML_READER *handle, const WS_XML_READER_ENCODING * case WS_XML_READER_INPUT_TYPE_BUFFER: { WS_XML_READER_BUFFER_INPUT *buf = (WS_XML_READER_BUFFER_INPUT *)input; - set_input_buffer( reader, NULL, (const unsigned char *)buf->encodedData + offset, - buf->encodedDataSize - offset ); + set_input_buffer( reader, (const unsigned char *)buf->encodedData + offset, buf->encodedDataSize - offset ); + break; + } + case WS_XML_READER_INPUT_TYPE_STREAM: + { + WS_XML_READER_STREAM_INPUT *stream = (WS_XML_READER_STREAM_INPUT *)input; + if (!reader->stream_buf && !(reader->stream_buf = heap_alloc( STREAM_BUFSIZE ))) + { + hr = E_OUTOFMEMORY; + goto done; + } + set_input_stream( reader, stream->readCallback, stream->readCallbackState ); break; } default: @@ -6969,6 +7040,24 @@ done: return hr; }
+static void set_input_xml_buffer( struct reader *reader, struct xmlbuf *buf ) +{ + reader->input_type = WS_XML_READER_INPUT_TYPE_BUFFER; + reader->input_buf = buf; + reader->input_enc = buf->encoding; + reader->input_charset = buf->charset; + reader->input_data = buf->bytes.bytes; + reader->input_size = buf->bytes.length; + reader->dict_static = buf->dict_static; + reader->dict = buf->dict; + + reader->read_size = reader->input_size; + reader->read_pos = 0; + reader->read_bufptr = reader->input_data; + + reader->text_conv_offset = 0; +} + /************************************************************************** * WsSetInputToBuffer [webservices.@] */ @@ -7003,12 +7092,7 @@ HRESULT WINAPI WsSetInputToBuffer( WS_XML_READER *handle, WS_XML_BUFFER *buffer, }
if ((hr = init_reader( reader )) != S_OK) goto done; - - reader->input_enc = xmlbuf->encoding; - reader->input_charset = xmlbuf->charset; - reader->dict_static = xmlbuf->dict_static; - reader->dict = xmlbuf->dict; - set_input_buffer( reader, xmlbuf, xmlbuf->bytes.bytes, xmlbuf->bytes.length ); + set_input_xml_buffer( reader, xmlbuf );
if (!(node = alloc_node( WS_XML_NODE_TYPE_BOF ))) hr = E_OUTOFMEMORY; else read_insert_bof( reader, node ); diff --git a/dlls/webservices/tests/reader.c b/dlls/webservices/tests/reader.c index b21e8396be..2b0475fddf 100644 --- a/dlls/webservices/tests/reader.c +++ b/dlls/webservices/tests/reader.c @@ -6689,6 +6689,111 @@ static void test_empty_text_field(void) WsFreeHeap( heap ); }
+static const char stream_utf8[] = {0xef,0xbb,0xbf,'<','t','/','>',0}; +static const struct stream_test +{ + const char *xml; + HRESULT hr; + int todo; +} +stream_tests[] = +{ + { "", WS_E_QUOTA_EXCEEDED }, + { "<?xml version=\"1.0\" encoding=\"utf-8\"?><t/>", S_OK }, + { "<t/>", S_OK }, + { stream_utf8, S_OK, 1 }, +}; + +static CALLBACK HRESULT read_callback( void *state, void *buf, ULONG buflen, ULONG *retlen, + const WS_ASYNC_CONTEXT *ctx, WS_ERROR *error ) +{ + struct stream_test *test = state; + ULONG len = strlen( test->xml ); + + ok( state != NULL, "NULL state\n" ); + ok( buf != NULL, "NULL buf\n" ); + ok( buflen > 0, "zero buflen\n" ); + ok( retlen != NULL, "NULL retlen\n" ); + if (buflen < len) return WS_E_QUOTA_EXCEEDED; + memcpy( buf, test->xml, len ); + *retlen = len; + return S_OK; +} + +static void test_stream_input(void) +{ + static WS_XML_STRING str_ns = {0, NULL}, str_t = {1, (BYTE *)"t"}; + WS_XML_READER_TEXT_ENCODING text = {{WS_XML_READER_ENCODING_TYPE_TEXT}}; + WS_XML_READER_STREAM_INPUT stream; + WS_XML_READER *reader; + const WS_XML_NODE *node; + WS_CHARSET charset; + HRESULT hr; + BOOL found; + ULONG i, size; + + hr = WsCreateReader( NULL, 0, &reader, NULL ); + ok( hr == S_OK, "got %08x\n", hr ); + + stream.input.inputType = WS_XML_READER_INPUT_TYPE_STREAM; + stream.readCallback = read_callback; + stream.readCallbackState = (void *)&stream_tests[2]; + hr = WsSetInput( reader, &text.encoding, &stream.input, NULL, 0, NULL ); + ok( hr == S_OK, "got %08x\n", hr ); + + size = sizeof(charset); + hr = WsGetReaderProperty( reader, WS_XML_READER_PROPERTY_CHARSET, &charset, size, NULL ); + todo_wine ok( hr == WS_E_QUOTA_EXCEEDED, "got %08x\n", hr ); + + hr = WsSetInput( reader, &text.encoding, &stream.input, NULL, 0, NULL ); + ok( hr == S_OK, "got %08x\n", hr ); + + hr = WsFillReader( reader, strlen(stream_tests[2].xml), NULL, NULL ); + ok( hr == S_OK, "got %08x\n", hr ); + + charset = 0xdeadbeef; + size = sizeof(charset); + hr = WsGetReaderProperty( reader, WS_XML_READER_PROPERTY_CHARSET, &charset, size, NULL ); + ok( hr == S_OK, "got %08x\n", hr ); + ok( charset == WS_CHARSET_UTF8, "got %u\n", charset ); + + found = -1; + hr = WsReadToStartElement( reader, &str_t, &str_ns, &found, NULL ); + ok( hr == S_OK, "got %08x\n", hr ); + + hr = WsFillReader( reader, 1, NULL, NULL ); + ok( hr == S_OK, "got %08x\n", hr ); + + hr = WsReadStartElement( reader, NULL ); + ok( hr == S_OK, "got %08x\n", hr ); + + for (i = 0; i < ARRAY_SIZE(stream_tests); i++) + { + stream.readCallbackState = (void *)&stream_tests[i]; + hr = WsSetInput( reader, &text.encoding, &stream.input, NULL, 0, NULL ); + ok( hr == S_OK, "%u: got %08x\n", i, hr ); + + hr = WsFillReader( reader, strlen( stream_tests[i].xml ), NULL, NULL ); + ok( hr == S_OK, "%u: got %08x\n", i, hr ); + + found = -1; + hr = WsReadToStartElement( reader, &str_t, &str_ns, &found, NULL ); + todo_wine_if(stream_tests[i].todo) ok( hr == stream_tests[i].hr, "%u: got %08x\n", i, hr ); + if (hr == S_OK) + { + ok( found == TRUE, "%u: got %d\n", i, found ); + hr = WsReadStartElement( reader, NULL ); + ok( hr == S_OK, "%u: got %08x\n", i, hr ); + + hr = WsGetReaderNode( reader, &node, NULL ); + ok( hr == S_OK, "%u: got %08x\n", i, hr ); + if (node) ok( node->nodeType == WS_XML_NODE_TYPE_END_ELEMENT, "%u: got %u\n", i, node->nodeType ); + } + } + + WsFreeReader( reader ); +} + START_TEST(reader) { test_WsCreateError(); @@ -6738,4 +6843,5 @@ START_TEST(reader) test_float(); test_repeating_element_choice(); test_empty_text_field(); + test_stream_input(); }