[6/6] webservices: Add support for writing attributes and text in binary mode.

Hans Leidekker hans at codeweavers.com
Tue May 23 05:03:54 CDT 2017


Signed-off-by: Hans Leidekker <hans at codeweavers.com>
---
 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 7ee0325985..3e8dad4e3e 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 e1b8c9e4a3..adf110dae6 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.0




More information about the wine-patches mailing list