From e6b9f9a63483dba5de8dc33690c0f42e9c1e903e Mon Sep 17 00:00:00 2001 From: Hans Leidekker Date: Tue, 23 May 2017 12:03:54 +0200 Subject: [PATCH] webservices: Add support for writing attributes and text in binary mode. Signed-off-by: Hans Leidekker Signed-off-by: Alexandre Julliard --- dlls/webservices/tests/writer.c | 100 ++++++++++++-- dlls/webservices/writer.c | 284 +++++++++++++++++++++++++++++----------- 2 files changed, 302 insertions(+), 82 deletions(-) diff --git a/dlls/webservices/tests/writer.c b/dlls/webservices/tests/writer.c index 7ee03259850..3e8dad4e3ea 100644 --- a/dlls/webservices/tests/writer.c +++ b/dlls/webservices/tests/writer.c @@ -3420,17 +3420,46 @@ static void test_WsWriteCharsUtf8(void) WsFreeWriter( writer ); } +static void check_output_bin( WS_XML_WRITER *writer, const char *expected, int len, unsigned int line ) +{ + WS_BYTES bytes; + ULONG size = sizeof(bytes); + HRESULT hr; + + memset( &bytes, 0, sizeof(bytes) ); + hr = WsGetWriterProperty( writer, WS_XML_WRITER_PROPERTY_BYTES, &bytes, size, NULL ); + ok( hr == S_OK, "%u: got %08x\n", line, hr ); + ok( bytes.length == len, "%u: got %u expected %u\n", line, bytes.length, len ); + if (bytes.length != len) return; + ok( !memcmp( bytes.bytes, expected, bytes.length ), "%u: got %s expected %s\n", line, + debugstr_bytes(bytes.bytes, bytes.length), debugstr_bytes((const BYTE *)expected, len) ); +} + static void test_binary_encoding(void) { - static const char res[] = {0x40,0x01,'t',0x01,0}; - static const char res2[] = {0x6d,0x01,'t',0x09,0x01,'p',0x02,'n','s',0x01,0}; - static const char res3[] = {0x41,0x02,'p','2',0x01,'t',0x09,0x02,'p','2',0x02,'n','s',0x01,0}; + static const char res[] = + {0x40,0x01,'t',0x01}; + static const char res2[] = + {0x6d,0x01,'t',0x09,0x01,'p',0x02,'n','s',0x01}; + static const char res3[] = + {0x41,0x02,'p','2',0x01,'t',0x09,0x02,'p','2',0x02,'n','s',0x01}; + static const char res4[] = + {0x41,0x02,'p','2',0x01,'t',0x09,0x02,'p','2',0x02,'n','s',0x99,0x04,'t','e','s','t'}; + static const char res100[] = + {0x40,0x01,'t',0x04,0x01,'t',0x98,0x00,0x01}; + static const char res101[] = + {0x40,0x01,'t',0x35,0x01,'t',0x98,0x00,0x09,0x01,'p',0x02,'n','s',0x01}; + static const char res102[] = + {0x40,0x01,'t',0x05,0x02,'p','2',0x01,'t',0x98,0x00,0x09,0x02,'p','2',0x02,'n','s',0x01}; + static const char res103[] = + {0x40,0x01,'t',0x05,0x02,'p','2',0x01,'t',0x98,0x04,'t','e','s','t',0x09,0x02,'p','2',0x02,'n','s',0x01}; WS_XML_WRITER_BINARY_ENCODING bin = {{WS_XML_WRITER_ENCODING_TYPE_BINARY}}; WS_XML_WRITER_BUFFER_OUTPUT buf = {{WS_XML_WRITER_OUTPUT_TYPE_BUFFER}}; static const char prefix[] = "p", prefix2[] = "p2"; - static const char localname[] = "t", empty[] = "", ns[] = "ns"; + static const char localname[] = "t", ns[] = "ns"; const WS_XML_STRING *prefix_ptr, *localname_ptr, *ns_ptr; - WS_XML_STRING str, str2, str3; + WS_XML_STRING str, str2, str3, localname2 = {1, (BYTE *)"t"}, empty = {0, NULL}; + WS_XML_UTF8_TEXT utf8 = {{WS_XML_TEXT_TYPE_UTF8}}; WS_XML_WRITER *writer; HRESULT hr; ULONG i; @@ -3439,13 +3468,32 @@ static void test_binary_encoding(void) const char *prefix; const char *localname; const char *ns; + const char *text; const char *result; + int len_result; } elem_tests[] = { - { NULL, localname, empty, res }, /* short element */ - { prefix, localname, ns, res2 }, /* one character prefix element */ - { prefix2, localname, ns, res3 }, /* element */ + { NULL, localname, "", NULL, res, sizeof(res) }, /* short element */ + { prefix, localname, ns, NULL, res2, sizeof(res2) }, /* one character prefix element */ + { prefix2, localname, ns, NULL, res3, sizeof(res3) }, /* element */ + { prefix2, localname, ns, "test", res4, sizeof(res4) }, /* element with text */ + }; + static const struct + { + const char *prefix; + const char *localname; + const char *ns; + const char *value; + const char *result; + int len_result; + } + attr_tests[] = + { + { NULL, localname, "", NULL, res100, sizeof(res100) }, /* short attribute */ + { prefix, localname, ns, NULL, res101, sizeof(res101) }, /* one character prefix attribute */ + { prefix2, localname, ns, NULL, res102, sizeof(res102) }, /* attribute */ + { prefix2, localname, ns, "test", res103, sizeof(res103) }, /* attribute with value */ }; hr = WsCreateWriter( NULL, 0, &writer, NULL ); @@ -3462,9 +3510,43 @@ static void test_binary_encoding(void) hr = WsWriteStartElement( writer, prefix_ptr, localname_ptr, ns_ptr, NULL ); ok( hr == S_OK, "%u: got %08x\n", i, hr ); + if (elem_tests[i].text) + { + utf8.value.length = strlen( elem_tests[i].text ); + utf8.value.bytes = (BYTE *)elem_tests[i].text; + hr = WsWriteText( writer, &utf8.text, 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 ); + if (hr == S_OK) check_output_bin( writer, elem_tests[i].result, elem_tests[i].len_result, __LINE__ ); + } + + for (i = 0; i < sizeof(attr_tests)/sizeof(attr_tests[0]); i++) + { + hr = WsSetOutput( writer, &bin.encoding, &buf.output, NULL, 0, NULL ); + ok( hr == S_OK, "%u: got %08x\n", i, hr ); + + prefix_ptr = init_xmlstring( attr_tests[i].prefix, &str ); + localname_ptr = init_xmlstring( attr_tests[i].localname, &str2 ); + ns_ptr = init_xmlstring( elem_tests[i].ns, &str3 ); + + hr = WsWriteStartElement( writer, NULL, &localname2, &empty, NULL ); + ok( hr == S_OK, "%u: got %08x\n", i, hr ); + hr = WsWriteStartAttribute( writer, prefix_ptr, localname_ptr, ns_ptr, FALSE, NULL ); + ok( hr == S_OK, "%u: got %08x\n", i, hr ); + if (attr_tests[i].value) + { + utf8.value.length = strlen( attr_tests[i].value ); + utf8.value.bytes = (BYTE *)attr_tests[i].value; + hr = WsWriteText( writer, &utf8.text, NULL ); + ok( hr == S_OK, "%u: got %08x\n", i, hr ); + } + hr = WsWriteEndAttribute( writer, NULL ); + ok( hr == S_OK, "got %08x\n", hr ); hr = WsWriteEndElement( writer, NULL ); ok( hr == S_OK, "%u: got %08x\n", i, hr ); - if (hr == S_OK) check_output( writer, elem_tests[i].result, __LINE__ ); + if (hr == S_OK) check_output_bin( writer, attr_tests[i].result, attr_tests[i].len_result, __LINE__ ); } WsFreeWriter( writer ); diff --git a/dlls/webservices/writer.c b/dlls/webservices/writer.c index e1b8c9e4a35..adf110dae6b 100644 --- a/dlls/webservices/writer.c +++ b/dlls/webservices/writer.c @@ -525,9 +525,19 @@ static HRESULT write_bytes_escape( struct writer *writer, const BYTE *bytes, ULO return S_OK; } -static HRESULT write_attribute( struct writer *writer, WS_XML_ATTRIBUTE *attr ) +static HRESULT write_attribute_value_text( struct writer *writer, const WS_XML_TEXT *text, BOOL single ) +{ + WS_XML_UTF8_TEXT *utf8 = (WS_XML_UTF8_TEXT *)text; + const struct escape *escapes[3]; + + escapes[0] = single ? &escape_apos : &escape_quot; + escapes[1] = &escape_lt; + escapes[2] = &escape_amp; + return write_bytes_escape( writer, utf8->value.bytes, utf8->value.length, escapes, 3 ); +} + +static HRESULT write_attribute_text( struct writer *writer, const WS_XML_ATTRIBUTE *attr ) { - WS_XML_UTF8_TEXT *text = (WS_XML_UTF8_TEXT *)attr->value; unsigned char quote = attr->singleQuote ? '\'' : '"'; const WS_XML_STRING *prefix = NULL; ULONG size; @@ -539,7 +549,6 @@ static HRESULT write_attribute( struct writer *writer, WS_XML_ATTRIBUTE *attr ) size = attr->localName->length + 4 /* ' =""' */; if (prefix && prefix->length) size += prefix->length + 1 /* ':' */; - if (text) size += text->value.length; if ((hr = write_grow_buffer( writer, size )) != S_OK) return hr; write_char( writer, ' ' ); @@ -551,19 +560,156 @@ static HRESULT write_attribute( struct writer *writer, WS_XML_ATTRIBUTE *attr ) write_bytes( writer, attr->localName->bytes, attr->localName->length ); write_char( writer, '=' ); write_char( writer, quote ); - if (text) - { - const struct escape *escapes[3]; - escapes[0] = attr->singleQuote ? &escape_apos : &escape_quot; - escapes[1] = &escape_lt; - escapes[2] = &escape_amp; - hr = write_bytes_escape( writer, text->value.bytes, text->value.length, escapes, 3 ); - } + if (attr->value) hr = write_attribute_value_text( writer, attr->value, attr->singleQuote ); write_char( writer, quote ); return hr; } +static enum record_type get_attr_record_type( const WS_XML_ATTRIBUTE *attr ) +{ + if (!attr->prefix || !attr->prefix->length) return RECORD_SHORT_ATTRIBUTE; + if (attr->prefix->length == 1 && attr->prefix->bytes[0] >= 'a' && attr->prefix->bytes[0] <= 'z') + { + return RECORD_PREFIX_ATTRIBUTE_A + attr->prefix->bytes[0] - 'a'; + } + return RECORD_ATTRIBUTE; +}; + +static HRESULT write_int31( struct writer *writer, ULONG len ) +{ + HRESULT hr; + + if ((hr = write_grow_buffer( writer, 1 )) != S_OK) return hr; + if (len < 0x80) + { + write_char( writer, len ); + return S_OK; + } + write_char( writer, (len & 0x7f) | 0x80 ); + + if ((hr = write_grow_buffer( writer, 1 )) != S_OK) return hr; + if ((len >>= 7) < 0x80) + { + write_char( writer, len ); + return S_OK; + } + write_char( writer, (len & 0x7f) | 0x80 ); + + if ((hr = write_grow_buffer( writer, 1 )) != S_OK) return hr; + if ((len >>= 7) < 0x80) + { + write_char( writer, len ); + return S_OK; + } + write_char( writer, (len & 0x7f) | 0x80 ); + + if ((hr = write_grow_buffer( writer, 1 )) != S_OK) return hr; + if ((len >>= 7) < 0x80) + { + write_char( writer, len ); + return S_OK; + } + write_char( writer, (len & 0x7f) | 0x80 ); + + if ((hr = write_grow_buffer( writer, 1 )) != S_OK) return hr; + if ((len >>= 7) < 0x08) + { + write_char( writer, len ); + return S_OK; + } + return WS_E_INVALID_FORMAT; +} + +static HRESULT write_string( struct writer *writer, const BYTE *bytes, ULONG len ) +{ + HRESULT hr; + if ((hr = write_int31( writer, len )) != S_OK) return hr; + if ((hr = write_grow_buffer( writer, len )) != S_OK) return hr; + write_bytes( writer, bytes, len ); + return S_OK; +} + +static enum record_type get_text_record_type( const WS_XML_TEXT *text, BOOL attr ) +{ + const WS_XML_UTF8_TEXT *utf8 = (const WS_XML_UTF8_TEXT *)text; + if (!utf8 || utf8->value.length <= 0xff) return attr ? RECORD_CHARS8_TEXT : RECORD_CHARS8_TEXT_WITH_ENDELEMENT; + return 0; +}; + +static HRESULT write_attribute_value_bin( struct writer *writer, const WS_XML_TEXT *text ) +{ + WS_XML_UTF8_TEXT *utf8 = (WS_XML_UTF8_TEXT *)text; + enum record_type type = get_text_record_type( text, TRUE ); + HRESULT hr; + + if ((hr = write_grow_buffer( writer, 1 )) != S_OK) return hr; + write_char( writer, type ); + + switch (type) + { + case RECORD_CHARS8_TEXT: + if ((hr = write_grow_buffer( writer, 1 )) != S_OK) return hr; + if (!utf8 || !utf8->value.length) write_char( writer, 0 ); + else + { + write_char( writer, utf8->value.length ); + if ((hr = write_grow_buffer( writer, utf8->value.length )) != S_OK) return hr; + write_bytes( writer, utf8->value.bytes, utf8->value.length ); + } + return S_OK; + + default: + ERR( "unhandled record type %u\n", type ); + return WS_E_NOT_SUPPORTED; + } +} + +static HRESULT write_attribute_bin( struct writer *writer, const WS_XML_ATTRIBUTE *attr ) +{ + enum record_type type = get_attr_record_type( attr ); + HRESULT hr; + + if ((hr = write_grow_buffer( writer, 1 )) != S_OK) return hr; + write_char( writer, type ); + + if (type >= RECORD_PREFIX_ATTRIBUTE_A && type <= RECORD_PREFIX_ATTRIBUTE_Z) + { + if ((hr = write_string( writer, attr->localName->bytes, attr->localName->length )) != S_OK) return hr; + return write_attribute_value_bin( writer, attr->value ); + } + + switch (type) + { + case RECORD_SHORT_ATTRIBUTE: + if ((hr = write_string( writer, attr->localName->bytes, attr->localName->length )) != S_OK) return hr; + break; + + case RECORD_ATTRIBUTE: + if ((hr = write_string( writer, attr->prefix->bytes, attr->prefix->length )) != S_OK) return hr; + if ((hr = write_string( writer, attr->localName->bytes, attr->localName->length )) != S_OK) return hr; + break; + + default: + ERR( "unhandled record type %u\n", type ); + return WS_E_NOT_SUPPORTED; + } + + return write_attribute_value_bin( writer, attr->value ); +} + +static HRESULT write_attribute( struct writer *writer, const WS_XML_ATTRIBUTE *attr ) +{ + switch (writer->output_enc) + { + case WS_XML_WRITER_ENCODING_TYPE_TEXT: return write_attribute_text( writer, attr ); + case WS_XML_WRITER_ENCODING_TYPE_BINARY: return write_attribute_bin( writer, attr ); + default: + ERR( "unhandled encoding %u\n", writer->output_enc ); + return WS_E_NOT_SUPPORTED; + } +} + static inline BOOL is_current_namespace( struct writer *writer, const WS_XML_STRING *ns ) { return (WsXmlStringEquals( writer->current_ns, ns, NULL ) == S_OK); @@ -647,60 +793,6 @@ static enum record_type get_xmlns_record_type( const WS_XML_ATTRIBUTE *attr ) return RECORD_XMLNS_ATTRIBUTE; }; -static HRESULT write_int31( struct writer *writer, ULONG len ) -{ - HRESULT hr; - - if ((hr = write_grow_buffer( writer, 1 )) != S_OK) return hr; - if (len < 0x80) - { - write_char( writer, len ); - return S_OK; - } - write_char( writer, (len & 0x7f) | 0x80 ); - - if ((hr = write_grow_buffer( writer, 1 )) != S_OK) return hr; - if ((len >>= 7) < 0x80) - { - write_char( writer, len ); - return S_OK; - } - write_char( writer, (len & 0x7f) | 0x80 ); - - if ((hr = write_grow_buffer( writer, 1 )) != S_OK) return hr; - if ((len >>= 7) < 0x80) - { - write_char( writer, len ); - return S_OK; - } - write_char( writer, (len & 0x7f) | 0x80 ); - - if ((hr = write_grow_buffer( writer, 1 )) != S_OK) return hr; - if ((len >>= 7) < 0x80) - { - write_char( writer, len ); - return S_OK; - } - write_char( writer, (len & 0x7f) | 0x80 ); - - if ((hr = write_grow_buffer( writer, 1 )) != S_OK) return hr; - if ((len >>= 7) < 0x08) - { - write_char( writer, len ); - return S_OK; - } - return WS_E_INVALID_FORMAT; -} - -static HRESULT write_string( struct writer *writer, const BYTE *bytes, ULONG len ) -{ - HRESULT hr; - if ((hr = write_int31( writer, len )) != S_OK) return hr; - if ((hr = write_grow_buffer( writer, len )) != S_OK) return hr; - write_bytes( writer, bytes, len ); - return S_OK; -} - static HRESULT write_namespace_attribute_bin( struct writer *writer, const WS_XML_ATTRIBUTE *attr ) { enum record_type type = get_xmlns_record_type( attr ); @@ -1003,6 +1095,7 @@ static HRESULT write_endelement_text( struct writer *writer, const WS_XML_ELEMEN static HRESULT write_endelement_bin( struct writer *writer ) { HRESULT hr; + if (node_type( writer->current ) == WS_XML_NODE_TYPE_TEXT) return S_OK; if ((hr = write_grow_buffer( writer, 1 )) != S_OK) return hr; write_char( writer, RECORD_ENDELEMENT ); return S_OK; @@ -1138,7 +1231,7 @@ static HRESULT write_add_attribute( struct writer *writer, const WS_XML_STRING * if (!(attr = heap_alloc_zero( sizeof(*attr) ))) return E_OUTOFMEMORY; - if (!prefix && ns->length > 0) prefix = elem->prefix; + if (!prefix && ns->length) prefix = elem->prefix; attr->singleQuote = !!single; if (prefix && !(attr->prefix = alloc_xml_string( prefix->bytes, prefix->length ))) @@ -1859,13 +1952,11 @@ static HRESULT write_add_text_node( struct writer *writer, const WS_XML_TEXT *va return S_OK; } -static HRESULT write_text( struct writer *writer, ULONG offset ) +static HRESULT write_text_text( struct writer *writer, const WS_XML_TEXT *text, ULONG offset ) { - const WS_XML_TEXT_NODE *text = (const WS_XML_TEXT_NODE *)writer->current; - const WS_XML_UTF8_TEXT *utf8 = (const WS_XML_UTF8_TEXT *)text->text; + const WS_XML_UTF8_TEXT *utf8 = (const WS_XML_UTF8_TEXT *)text; HRESULT hr; - if (!writer->current->parent) return WS_E_INVALID_FORMAT; if (node_type( writer->current->parent ) == WS_XML_NODE_TYPE_ELEMENT) { const struct escape *escapes[3] = { &escape_lt, &escape_gt, &escape_amp }; @@ -1881,8 +1972,55 @@ static HRESULT write_text( struct writer *writer, ULONG offset ) return WS_E_INVALID_FORMAT; } +static HRESULT write_text_bin( struct writer *writer, const WS_XML_TEXT *text, ULONG offset ) +{ + const WS_XML_UTF8_TEXT *utf8 = (const WS_XML_UTF8_TEXT *)text; + enum record_type type = get_text_record_type( text, FALSE ); + HRESULT hr; + + if (offset) + { + FIXME( "no support for appending text in binary mode\n" ); + return WS_E_NOT_SUPPORTED; + } + + switch (type) + { + case RECORD_CHARS8_TEXT_WITH_ENDELEMENT: + if ((hr = write_grow_buffer( writer, 2 )) != S_OK) return hr; + write_char( writer, type ); + if (!utf8 || !utf8->value.length) write_char( writer, 0 ); + else + { + write_char( writer, utf8->value.length ); + if ((hr = write_grow_buffer( writer, utf8->value.length )) != S_OK) return hr; + write_bytes( writer, utf8->value.bytes, utf8->value.length ); + } + return S_OK; + + default: + FIXME( "unhandled record type %u\n", type ); + return WS_E_NOT_SUPPORTED; + } +} + +static HRESULT write_text( struct writer *writer, const WS_XML_TEXT *text, ULONG offset ) +{ + if (!writer->current->parent) return WS_E_INVALID_FORMAT; + + switch (writer->output_enc) + { + case WS_XML_WRITER_ENCODING_TYPE_TEXT: return write_text_text( writer, text, offset ); + case WS_XML_WRITER_ENCODING_TYPE_BINARY: return write_text_bin( writer, text, offset ); + default: + ERR( "unhandled encoding %u\n", writer->output_enc ); + return WS_E_NOT_SUPPORTED; + } +} + static HRESULT write_text_node( struct writer *writer, const WS_XML_TEXT *text ) { + WS_XML_TEXT_NODE *node = (WS_XML_TEXT_NODE *)writer->current; ULONG offset; HRESULT hr; @@ -1891,10 +2029,10 @@ static HRESULT write_text_node( struct writer *writer, const WS_XML_TEXT *text ) { offset = 0; if ((hr = write_add_text_node( writer, text )) != S_OK) return hr; + node = (WS_XML_TEXT_NODE *)writer->current; } else { - WS_XML_TEXT_NODE *node = (WS_XML_TEXT_NODE *)writer->current; WS_XML_UTF8_TEXT *new, *old = (WS_XML_UTF8_TEXT *)node->text; offset = old->value.length; @@ -1903,7 +2041,7 @@ static HRESULT write_text_node( struct writer *writer, const WS_XML_TEXT *text ) node->text = &new->text; } - if ((hr = write_text( writer, offset )) != S_OK) return hr; + if ((hr = write_text( writer, node->text, offset )) != S_OK) return hr; writer->state = WRITER_STATE_TEXT; return S_OK; @@ -3539,7 +3677,7 @@ static HRESULT write_tree_node( struct writer *writer ) case WS_XML_NODE_TYPE_TEXT: if (writer->state == WRITER_STATE_STARTELEMENT && (hr = write_endstartelement( writer )) != S_OK) return hr; - if ((hr = write_text( writer, 0 )) != S_OK) return hr; + if ((hr = write_text( writer, ((const WS_XML_TEXT_NODE *)writer->current)->text, 0 )) != S_OK) return hr; writer->state = WRITER_STATE_TEXT; return S_OK; -- 2.11.4.GIT