webservices: Add support for writing attributes and text in binary mode.

Signed-off-by: Hans Leidekker <hans@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Hans Leidekker 2017-05-23 12:03:54 +02:00 committed by Alexandre Julliard
parent e091c1ec1a
commit e6b9f9a634
2 changed files with 302 additions and 82 deletions

View file

@ -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( writer, elem_tests[i].result, __LINE__ );
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_bin( writer, attr_tests[i].result, attr_tests[i].len_result, __LINE__ );
}
WsFreeWriter( writer );

View file

@ -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;