[PATCH 5/5] webservices: Don't use the dictionary for UTF-16 text.

Hans Leidekker hans at codeweavers.com
Fri Dec 8 08:49:41 CST 2017


Although UTF-16 text is converted to UTF-8 before transmission it's stored inline
rather than in the dictionary.

Signed-off-by: Hans Leidekker <hans at codeweavers.com>
---
 dlls/webservices/reader.c              |   12 +
 dlls/webservices/webservices_private.h |    1 +
 dlls/webservices/writer.c              | 1411 +++++++++++++++++---------------
 3 files changed, 754 insertions(+), 670 deletions(-)

diff --git a/dlls/webservices/reader.c b/dlls/webservices/reader.c
index 9d024a3446..a05b1edb33 100644
--- a/dlls/webservices/reader.c
+++ b/dlls/webservices/reader.c
@@ -788,6 +788,18 @@ WS_XML_UTF8_TEXT *alloc_utf8_text( const BYTE *data, ULONG len )
     return ret;
 }
 
+WS_XML_UTF16_TEXT *alloc_utf16_text( const BYTE *data, ULONG len )
+{
+    WS_XML_UTF16_TEXT *ret;
+
+    if (!(ret = heap_alloc( sizeof(*ret) + len ))) return NULL;
+    ret->text.textType = WS_XML_TEXT_TYPE_UTF16;
+    ret->byteCount     = len;
+    ret->bytes         = len ? (BYTE *)(ret + 1) : NULL;
+    if (data) memcpy( ret->bytes, data, len );
+    return ret;
+}
+
 WS_XML_BASE64_TEXT *alloc_base64_text( const BYTE *data, ULONG len )
 {
     WS_XML_BASE64_TEXT *ret;
diff --git a/dlls/webservices/webservices_private.h b/dlls/webservices/webservices_private.h
index f8cc6d9cfe..e477e0fc3f 100644
--- a/dlls/webservices/webservices_private.h
+++ b/dlls/webservices/webservices_private.h
@@ -68,6 +68,7 @@ HRESULT read_header( WS_XML_READER *, const WS_XML_STRING *, const WS_XML_STRING
 HRESULT create_header_buffer( WS_XML_READER *, WS_HEAP *, WS_XML_BUFFER ** ) DECLSPEC_HIDDEN;
 
 WS_XML_UTF8_TEXT *alloc_utf8_text( const BYTE *, ULONG ) DECLSPEC_HIDDEN;
+WS_XML_UTF16_TEXT *alloc_utf16_text( const BYTE *, ULONG ) DECLSPEC_HIDDEN;
 WS_XML_BASE64_TEXT *alloc_base64_text( const BYTE *, ULONG ) DECLSPEC_HIDDEN;
 WS_XML_BOOL_TEXT *alloc_bool_text( BOOL ) DECLSPEC_HIDDEN;
 WS_XML_INT32_TEXT *alloc_int32_text( INT32 ) DECLSPEC_HIDDEN;
diff --git a/dlls/webservices/writer.c b/dlls/webservices/writer.c
index 4b2466390c..be2994af83 100644
--- a/dlls/webservices/writer.c
+++ b/dlls/webservices/writer.c
@@ -667,6 +667,15 @@ static enum record_type get_attr_text_record_type( const WS_XML_TEXT *text, BOOL
         if (text_utf8->value.length <= MAX_UINT16) return RECORD_CHARS16_TEXT;
         return RECORD_CHARS32_TEXT;
     }
+    case WS_XML_TEXT_TYPE_UTF16:
+    {
+        const WS_XML_UTF16_TEXT *text_utf16 = (const WS_XML_UTF16_TEXT *)text;
+        int len = text_utf16->byteCount / sizeof(WCHAR);
+        int len_utf8 = WideCharToMultiByte( CP_UTF8, 0, (const WCHAR *)text_utf16->bytes, len, NULL, 0, NULL, NULL );
+        if (len_utf8 <= MAX_UINT8) return RECORD_CHARS8_TEXT;
+        if (len_utf8 <= MAX_UINT16) return RECORD_CHARS16_TEXT;
+        return RECORD_CHARS32_TEXT;
+    }
     case WS_XML_TEXT_TYPE_BASE64:
     {
         const WS_XML_BASE64_TEXT *text_base64 = (const WS_XML_BASE64_TEXT *)text;
@@ -784,153 +793,566 @@ static BOOL get_string_id( struct writer *writer, const WS_XML_STRING *str, ULON
     return FALSE;
 }
 
-static HRESULT write_attribute_value_bin( struct writer *writer, const WS_XML_TEXT *text )
+static ULONG format_bool( const BOOL *ptr, unsigned char *buf )
 {
-    enum record_type type;
-    BOOL use_dict = FALSE;
-    HRESULT hr;
-    ULONG id;
-
-    if (text && text->textType == WS_XML_TEXT_TYPE_UTF8)
+    static const unsigned char bool_true[] = {'t','r','u','e'}, bool_false[] = {'f','a','l','s','e'};
+    if (*ptr)
     {
-        const WS_XML_UTF8_TEXT *utf8 = (const WS_XML_UTF8_TEXT *)text;
-        use_dict = get_string_id( writer, &utf8->value, &id );
+        memcpy( buf, bool_true, sizeof(bool_true) );
+        return sizeof(bool_true);
     }
-    type = get_attr_text_record_type( text, use_dict );
+    memcpy( buf, bool_false, sizeof(bool_false) );
+    return sizeof(bool_false);
+}
 
-    if ((hr = write_grow_buffer( writer, 1 )) != S_OK) return hr;
-    write_char( writer, type );
+static ULONG format_int32( const INT32 *ptr, unsigned char *buf )
+{
+    return wsprintfA( (char *)buf, "%d", *ptr );
+}
 
-    switch (type)
+static ULONG format_int64( const INT64 *ptr, unsigned char *buf )
+{
+    return wsprintfA( (char *)buf, "%I64d", *ptr );
+}
+
+static ULONG format_uint64( const UINT64 *ptr, unsigned char *buf )
+{
+    return wsprintfA( (char *)buf, "%I64u", *ptr );
+}
+
+static ULONG format_double( const double *ptr, unsigned char *buf )
+{
+#ifdef HAVE_POWL
+    static const long double precision = 0.0000000000000001;
+    unsigned char *p = buf;
+    long double val = *ptr;
+    int neg, mag, mag2, use_exp;
+
+    if (isnan( val ))
     {
-    case RECORD_CHARS8_TEXT:
+        memcpy( buf, "NaN", 3 );
+        return 3;
+    }
+    if (isinf( val ))
     {
-        WS_XML_UTF8_TEXT *text_utf8 = (WS_XML_UTF8_TEXT *)text;
-        if (!text_utf8)
+        if (val < 0)
         {
-            if ((hr = write_grow_buffer( writer, 1 )) != S_OK) return hr;
-            write_char( writer, 0 );
-            return S_OK;
+            memcpy( buf, "-INF", 4 );
+            return 4;
         }
-        if ((hr = write_grow_buffer( writer, 1 + text_utf8->value.length )) != S_OK) return hr;
-        write_char( writer, text_utf8->value.length );
-        write_bytes( writer, text_utf8->value.bytes, text_utf8->value.length );
-        return S_OK;
+        memcpy( buf, "INF", 3 );
+        return 3;
     }
-    case RECORD_CHARS16_TEXT:
+    if (val == 0.0)
     {
-        WS_XML_UTF8_TEXT *text_utf8 = (WS_XML_UTF8_TEXT *)text;
-        UINT16 len = text_utf8->value.length;
-        if ((hr = write_grow_buffer( writer, sizeof(len) + len )) != S_OK) return hr;
-        write_bytes( writer, (const BYTE *)&len, sizeof(len) );
-        write_bytes( writer, text_utf8->value.bytes, len );
-        return S_OK;
+        *p = '0';
+        return 1;
     }
-    case RECORD_BYTES8_TEXT:
+
+    if ((neg = val < 0))
     {
-        WS_XML_BASE64_TEXT *text_base64 = (WS_XML_BASE64_TEXT *)text;
-        if ((hr = write_grow_buffer( writer, 1 + text_base64->length )) != S_OK) return hr;
-        write_char( writer, text_base64->length );
-        write_bytes( writer, text_base64->bytes, text_base64->length );
-        return S_OK;
+        *p++ = '-';
+        val = -val;
     }
-    case RECORD_BYTES16_TEXT:
+
+    mag = log10l( val );
+    use_exp = (mag >= 15 || (neg && mag >= 1) || mag <= -1);
+    if (use_exp)
     {
-        WS_XML_BASE64_TEXT *text_base64 = (WS_XML_BASE64_TEXT *)text;
-        UINT16 len = text_base64->length;
-        if ((hr = write_grow_buffer( writer, sizeof(len) + len )) != S_OK) return hr;
-        write_bytes( writer, (const BYTE *)&len, sizeof(len) );
-        write_bytes( writer, text_base64->bytes, len );
-        return S_OK;
+        if (mag < 0) mag -= 1;
+        val = val / powl( 10.0, mag );
+        mag2 = mag;
+        mag = 0;
     }
-    case RECORD_ZERO_TEXT:
-    case RECORD_ONE_TEXT:
-    case RECORD_FALSE_TEXT:
-    case RECORD_TRUE_TEXT:
-        return S_OK;
+    else if (mag < 1) mag = 0;
 
-    case RECORD_INT8_TEXT:
+    while (val > precision || mag >= 0)
     {
-        INT8 val = get_text_value_int( text );
-        if ((hr = write_grow_buffer( writer, sizeof(val) )) != S_OK) return hr;
-        write_char( writer, val );
-        return S_OK;
+        long double weight = powl( 10.0, mag );
+        if (weight > 0 && !isinf( weight ))
+        {
+            int digit = floorl( val / weight );
+            val -= digit * weight;
+            *(p++) = '0' + digit;
+        }
+        if (!mag && val > precision) *(p++) = '.';
+        mag--;
     }
-    case RECORD_INT16_TEXT:
+
+    if (use_exp)
     {
-        INT16 val = get_text_value_int( text );
-        if ((hr = write_grow_buffer( writer, sizeof(val) )) != S_OK) return hr;
-        write_bytes( writer, (const BYTE *)&val, sizeof(val) );
-        return S_OK;
+        int i, j;
+        *(p++) = 'E';
+        if (mag2 > 0) *(p++) = '+';
+        else
+        {
+            *(p++) = '-';
+            mag2 = -mag2;
+        }
+        mag = 0;
+        while (mag2 > 0)
+        {
+            *(p++) = '0' + mag2 % 10;
+            mag2 /= 10;
+            mag++;
+        }
+        for (i = -mag, j = -1; i < j; i++, j--)
+        {
+            p[i] ^= p[j];
+            p[j] ^= p[i];
+            p[i] ^= p[j];
+        }
     }
-    case RECORD_INT32_TEXT:
+
+    return p - buf;
+#else
+    FIXME( "powl not found at build time\n" );
+    return 0;
+#endif
+}
+
+static inline int year_size( int year )
+{
+    return leap_year( year ) ? 366 : 365;
+}
+
+#define TZ_OFFSET 8
+static ULONG format_datetime( const WS_DATETIME *ptr, unsigned char *buf )
+{
+    static const char fmt[] = "%04u-%02u-%02uT%02u:%02u:%02u";
+    int day, hour, min, sec, sec_frac, month = 0, year = 1, tz_hour;
+    unsigned __int64 ticks, day_ticks;
+    ULONG len;
+
+    if (ptr->format == WS_DATETIME_FORMAT_LOCAL &&
+        ptr->ticks >= TICKS_1601_01_01 + TZ_OFFSET * TICKS_PER_HOUR)
     {
-        INT32 val = get_text_value_int( text );
-        if ((hr = write_grow_buffer( writer, sizeof(val) )) != S_OK) return hr;
-        write_bytes( writer, (const BYTE *)&val, sizeof(val) );
-        return S_OK;
+        ticks = ptr->ticks - TZ_OFFSET * TICKS_PER_HOUR;
+        tz_hour = TZ_OFFSET;
     }
-    case RECORD_INT64_TEXT:
+    else
     {
-        INT64 val = get_text_value_int( text );
-        if ((hr = write_grow_buffer( writer, sizeof(val) )) != S_OK) return hr;
-        write_bytes( writer, (const BYTE *)&val, sizeof(val) );
-        return S_OK;
+        ticks = ptr->ticks;
+        tz_hour = 0;
     }
-    case RECORD_UINT64_TEXT:
+    day = ticks / TICKS_PER_DAY;
+    day_ticks = ticks % TICKS_PER_DAY;
+    hour = day_ticks / TICKS_PER_HOUR;
+    min = (day_ticks % TICKS_PER_HOUR) / TICKS_PER_MIN;
+    sec = (day_ticks % TICKS_PER_MIN) / TICKS_PER_SEC;
+    sec_frac = day_ticks % TICKS_PER_SEC;
+
+    while (day >= year_size( year ))
     {
-        WS_XML_UINT64_TEXT *text_uint64 = (WS_XML_UINT64_TEXT *)text;
-        if ((hr = write_grow_buffer( writer, sizeof(text_uint64->value) )) != S_OK) return hr;
-        write_bytes( writer, (const BYTE *)&text_uint64->value, sizeof(text_uint64->value) );
-        return S_OK;
+        day -= year_size( year );
+        year++;
     }
-    case RECORD_DOUBLE_TEXT:
+    while (day >= month_days[leap_year( year )][month])
     {
-        WS_XML_DOUBLE_TEXT *text_double = (WS_XML_DOUBLE_TEXT *)text;
-        if ((hr = write_grow_buffer( writer, sizeof(text_double->value) )) != S_OK) return hr;
-        write_bytes( writer, (const BYTE *)&text_double->value, sizeof(text_double->value) );
-        return S_OK;
+        day -= month_days[leap_year( year )][month];
+        month++;
     }
-    case RECORD_GUID_TEXT:
+
+    len = sprintf( (char *)buf, fmt, year, month + 1, day + 1, hour, min, sec );
+    if (sec_frac)
     {
-        WS_XML_GUID_TEXT *text_guid = (WS_XML_GUID_TEXT *)text;
-        if ((hr = write_grow_buffer( writer, sizeof(text_guid->value) )) != S_OK) return hr;
-        write_bytes( writer, (const BYTE *)&text_guid->value, sizeof(text_guid->value) );
-        return S_OK;
+        static const char fmt_frac[] = ".%07u";
+        len += sprintf( (char *)buf + len, fmt_frac, sec_frac );
+        while (buf[len - 1] == '0') len--;
     }
-    case RECORD_UNIQUE_ID_TEXT:
+    if (ptr->format == WS_DATETIME_FORMAT_UTC)
     {
-        WS_XML_UNIQUE_ID_TEXT *text_unique_id = (WS_XML_UNIQUE_ID_TEXT *)text;
-        if ((hr = write_grow_buffer( writer, sizeof(text_unique_id->value) )) != S_OK) return hr;
-        write_bytes( writer, (const BYTE *)&text_unique_id->value, sizeof(text_unique_id->value) );
-        return S_OK;
+        buf[len++] = 'Z';
     }
-    case RECORD_DATETIME_TEXT:
+    else if (ptr->format == WS_DATETIME_FORMAT_LOCAL)
     {
-        WS_XML_DATETIME_TEXT *text_datetime = (WS_XML_DATETIME_TEXT *)text;
-        UINT64 val = text_datetime->value.ticks;
+        static const char fmt_tz[] = "%c%02u:00";
+        len += sprintf( (char *)buf + len, fmt_tz, tz_hour ? '-' : '+', tz_hour );
+    }
 
-        assert( val <= TICKS_MAX );
-        if (text_datetime->value.format == WS_DATETIME_FORMAT_UTC) val |= (UINT64)1 << 62;
-        else if (text_datetime->value.format == WS_DATETIME_FORMAT_LOCAL) val |= (UINT64)1 << 63;
+    return len;
+}
 
-        if ((hr = write_grow_buffer( writer, sizeof(val) )) != S_OK) return hr;
-        write_bytes( writer, (const BYTE *)&val, sizeof(val) );
-        return S_OK;
-    }
-    default:
-        FIXME( "unhandled record type %02x\n", type );
-        return E_NOTIMPL;
-    }
+static ULONG format_guid( const GUID *ptr, unsigned char *buf )
+{
+    static const char fmt[] = "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x";
+    return sprintf( (char *)buf, fmt, ptr->Data1, ptr->Data2, ptr->Data3,
+                    ptr->Data4[0], ptr->Data4[1], ptr->Data4[2], ptr->Data4[3],
+                    ptr->Data4[4], ptr->Data4[5], ptr->Data4[6], ptr->Data4[7] );
 }
 
-static enum record_type get_attr_record_type( const WS_XML_ATTRIBUTE *attr, BOOL use_dict )
+static ULONG format_urn( const GUID *ptr, unsigned char *buf )
 {
-    if (!attr->prefix || !attr->prefix->length)
-    {
-        if (use_dict) return RECORD_SHORT_DICTIONARY_ATTRIBUTE;
-        return RECORD_SHORT_ATTRIBUTE;
-    }
+    static const char fmt[] = "urn:uuid:%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x";
+    return sprintf( (char *)buf, fmt, ptr->Data1, ptr->Data2, ptr->Data3,
+                    ptr->Data4[0], ptr->Data4[1], ptr->Data4[2], ptr->Data4[3],
+                    ptr->Data4[4], ptr->Data4[5], ptr->Data4[6], ptr->Data4[7] );
+}
+
+static ULONG format_qname( const WS_XML_STRING *prefix, const WS_XML_STRING *localname, unsigned char *buf )
+{
+    ULONG len = 0;
+    if (prefix && prefix->length)
+    {
+        memcpy( buf, prefix->bytes, prefix->length );
+        len += prefix->length;
+        buf[len++] = ':';
+    }
+    memcpy( buf + len, localname->bytes, localname->length );
+    return len + localname->length;
+}
+
+static ULONG encode_base64( const unsigned char *bin, ULONG len, unsigned char *buf )
+{
+    static const char base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+    ULONG i = 0, x;
+
+    while (len > 0)
+    {
+        buf[i++] = base64[(bin[0] & 0xfc) >> 2];
+        x = (bin[0] & 3) << 4;
+        if (len == 1)
+        {
+            buf[i++] = base64[x];
+            buf[i++] = '=';
+            buf[i++] = '=';
+            break;
+        }
+        buf[i++] = base64[x | ((bin[1] & 0xf0) >> 4)];
+        x = (bin[1] & 0x0f) << 2;
+        if (len == 2)
+        {
+            buf[i++] = base64[x];
+            buf[i++] = '=';
+            break;
+        }
+        buf[i++] = base64[x | ((bin[2] & 0xc0) >> 6)];
+        buf[i++] = base64[bin[2] & 0x3f];
+        bin += 3;
+        len -= 3;
+    }
+    return i;
+}
+
+static HRESULT text_to_utf8text( const WS_XML_TEXT *text, const WS_XML_UTF8_TEXT *old, ULONG *offset,
+                                 WS_XML_UTF8_TEXT **ret )
+{
+    ULONG len_old = old ? old->value.length : 0;
+    if (offset) *offset = len_old;
+
+    switch (text->textType)
+    {
+    case WS_XML_TEXT_TYPE_UTF8:
+    {
+        const WS_XML_UTF8_TEXT *src = (const WS_XML_UTF8_TEXT *)text;
+
+        if (!(*ret = alloc_utf8_text( NULL, len_old + src->value.length ))) return E_OUTOFMEMORY;
+        if (old) memcpy( (*ret)->value.bytes, old->value.bytes, len_old );
+        memcpy( (*ret)->value.bytes + len_old, src->value.bytes, src->value.length );
+        return S_OK;
+    }
+    case WS_XML_TEXT_TYPE_UTF16:
+    {
+        const WS_XML_UTF16_TEXT *src = (const WS_XML_UTF16_TEXT *)text;
+        const WCHAR *str = (const WCHAR *)src->bytes;
+        ULONG len = src->byteCount / sizeof(WCHAR), len_utf8;
+
+        if (src->byteCount % sizeof(WCHAR)) return E_INVALIDARG;
+        len_utf8 = WideCharToMultiByte( CP_UTF8, 0, str, len, NULL, 0, NULL, NULL );
+        if (!(*ret = alloc_utf8_text( NULL, len_old + len_utf8 ))) return E_OUTOFMEMORY;
+        if (old) memcpy( (*ret)->value.bytes, old->value.bytes, len_old );
+        WideCharToMultiByte( CP_UTF8, 0, str, len, (char *)(*ret)->value.bytes + len_old, len_utf8, NULL, NULL );
+        return S_OK;
+    }
+    case WS_XML_TEXT_TYPE_BASE64:
+    {
+        const WS_XML_BASE64_TEXT *base64 = (const WS_XML_BASE64_TEXT *)text;
+        ULONG len = ((4 * base64->length / 3) + 3) & ~3;
+
+        if (!(*ret = alloc_utf8_text( NULL, len_old + len ))) return E_OUTOFMEMORY;
+        if (old) memcpy( (*ret)->value.bytes, old->value.bytes, len_old );
+        (*ret)->value.length = encode_base64( base64->bytes, base64->length, (*ret)->value.bytes + len_old ) + len_old;
+        return S_OK;
+    }
+    case WS_XML_TEXT_TYPE_BOOL:
+    {
+        const WS_XML_BOOL_TEXT *bool_text = (const WS_XML_BOOL_TEXT *)text;
+
+        if (!(*ret = alloc_utf8_text( NULL, len_old + 5 ))) return E_OUTOFMEMORY;
+        if (old) memcpy( (*ret)->value.bytes, old->value.bytes, len_old );
+        (*ret)->value.length = format_bool( &bool_text->value, (*ret)->value.bytes + len_old ) + len_old;
+        return S_OK;
+    }
+    case WS_XML_TEXT_TYPE_INT32:
+    {
+        const WS_XML_INT32_TEXT *int32_text = (const WS_XML_INT32_TEXT *)text;
+        unsigned char buf[12]; /* "-2147483648" */
+        ULONG len = format_int32( &int32_text->value, buf );
+
+        if (!(*ret = alloc_utf8_text( NULL, len_old + len ))) return E_OUTOFMEMORY;
+        if (old) memcpy( (*ret)->value.bytes, old->value.bytes, len_old );
+        memcpy( (*ret)->value.bytes + len_old, buf, len );
+        return S_OK;
+    }
+    case WS_XML_TEXT_TYPE_INT64:
+    {
+        const WS_XML_INT64_TEXT *int64_text = (const WS_XML_INT64_TEXT *)text;
+        unsigned char buf[21]; /* "-9223372036854775808" */
+        ULONG len = format_int64( &int64_text->value, buf );
+
+        if (!(*ret = alloc_utf8_text( NULL, len_old + len ))) return E_OUTOFMEMORY;
+        if (old) memcpy( (*ret)->value.bytes, old->value.bytes, len_old );
+        memcpy( (*ret)->value.bytes + len_old, buf, len );
+        return S_OK;
+    }
+    case WS_XML_TEXT_TYPE_UINT64:
+    {
+        const WS_XML_UINT64_TEXT *uint64_text = (const WS_XML_UINT64_TEXT *)text;
+        unsigned char buf[21]; /* "18446744073709551615" */
+        ULONG len = format_uint64( &uint64_text->value, buf );
+
+        if (!(*ret = alloc_utf8_text( NULL, len_old + len ))) return E_OUTOFMEMORY;
+        if (old) memcpy( (*ret)->value.bytes, old->value.bytes, len_old );
+        memcpy( (*ret)->value.bytes + len_old, buf, len );
+        return S_OK;
+    }
+    case WS_XML_TEXT_TYPE_DOUBLE:
+    {
+        const WS_XML_DOUBLE_TEXT *double_text = (const WS_XML_DOUBLE_TEXT *)text;
+        unsigned char buf[32]; /* "-1.1111111111111111E-308", oversized to address Valgrind limitations */
+        unsigned short fpword;
+        ULONG len;
+
+        if (!set_fpword( 0x37f, &fpword )) return E_NOTIMPL;
+        len = format_double( &double_text->value, buf );
+        restore_fpword( fpword );
+        if (!len) return E_NOTIMPL;
+
+        if (!(*ret = alloc_utf8_text( NULL, len_old + len ))) return E_OUTOFMEMORY;
+        if (old) memcpy( (*ret)->value.bytes, old->value.bytes, len_old );
+        memcpy( (*ret)->value.bytes + len_old, buf, len );
+        return S_OK;
+    }
+    case WS_XML_TEXT_TYPE_GUID:
+    {
+        const WS_XML_GUID_TEXT *id = (const WS_XML_GUID_TEXT *)text;
+
+        if (!(*ret = alloc_utf8_text( NULL, len_old + 37 ))) return E_OUTOFMEMORY;
+        if (old) memcpy( (*ret)->value.bytes, old->value.bytes, len_old );
+        (*ret)->value.length = format_guid( &id->value, (*ret)->value.bytes + len_old ) + len_old;
+        return S_OK;
+    }
+    case WS_XML_TEXT_TYPE_UNIQUE_ID:
+    {
+        const WS_XML_UNIQUE_ID_TEXT *id = (const WS_XML_UNIQUE_ID_TEXT *)text;
+
+        if (!(*ret = alloc_utf8_text( NULL, len_old + 46 ))) return E_OUTOFMEMORY;
+        if (old) memcpy( (*ret)->value.bytes, old->value.bytes, len_old );
+        (*ret)->value.length = format_urn( &id->value, (*ret)->value.bytes + len_old ) + len_old;
+        return S_OK;
+    }
+    case WS_XML_TEXT_TYPE_DATETIME:
+    {
+        const WS_XML_DATETIME_TEXT *dt = (const WS_XML_DATETIME_TEXT *)text;
+
+        if (!(*ret = alloc_utf8_text( NULL, len_old + 34 ))) return E_OUTOFMEMORY;
+        if (old) memcpy( (*ret)->value.bytes, old->value.bytes, len_old );
+        (*ret)->value.length = format_datetime( &dt->value, (*ret)->value.bytes + len_old ) + len_old;
+        return S_OK;
+    }
+    case WS_XML_TEXT_TYPE_QNAME:
+    {
+        const WS_XML_QNAME_TEXT *qn = (const WS_XML_QNAME_TEXT *)text;
+        ULONG len = qn->localName->length;
+
+        if (qn->prefix && qn->prefix->length) len += qn->prefix->length + 1;
+        if (!(*ret = alloc_utf8_text( NULL, len_old + len ))) return E_OUTOFMEMORY;
+        if (old) memcpy( (*ret)->value.bytes, old->value.bytes, len_old );
+        (*ret)->value.length = format_qname( qn->prefix, qn->localName, (*ret)->value.bytes + len_old ) + len_old;
+        return S_OK;
+    }
+    default:
+        FIXME( "unhandled text type %u\n", text->textType );
+        return E_NOTIMPL;
+    }
+}
+
+static HRESULT write_attribute_value_bin( struct writer *writer, const WS_XML_TEXT *text )
+{
+    enum record_type type;
+    BOOL use_dict = FALSE;
+    HRESULT hr;
+    ULONG id;
+
+    if (text && text->textType == WS_XML_TEXT_TYPE_UTF8)
+    {
+        const WS_XML_UTF8_TEXT *utf8 = (const WS_XML_UTF8_TEXT *)text;
+        use_dict = get_string_id( writer, &utf8->value, &id );
+    }
+    type = get_attr_text_record_type( text, use_dict );
+
+    if ((hr = write_grow_buffer( writer, 1 )) != S_OK) return hr;
+    write_char( writer, type );
+
+    switch (type)
+    {
+    case RECORD_CHARS8_TEXT:
+    {
+        const WS_XML_UTF8_TEXT *text_utf8;
+        WS_XML_UTF8_TEXT *new = NULL;
+        UINT8 len;
+
+        if (!text)
+        {
+            if ((hr = write_grow_buffer( writer, 1 )) != S_OK) return hr;
+            write_char( writer, 0 );
+            return S_OK;
+        }
+        if (text->textType == WS_XML_TEXT_TYPE_UTF8) text_utf8 = (const WS_XML_UTF8_TEXT *)text;
+        else
+        {
+            if ((hr = text_to_utf8text( text, NULL, NULL, &new )) != S_OK) return hr;
+            text_utf8 = new;
+        }
+        len = text_utf8->value.length;
+        if ((hr = write_grow_buffer( writer, sizeof(len) + len )) != S_OK)
+        {
+            heap_free( new );
+            return hr;
+        }
+        write_char( writer, len );
+        write_bytes( writer, text_utf8->value.bytes, len );
+        heap_free( new );
+        return S_OK;
+    }
+    case RECORD_CHARS16_TEXT:
+    {
+        const WS_XML_UTF8_TEXT *text_utf8;
+        WS_XML_UTF8_TEXT *new = NULL;
+        UINT16 len;
+
+        if (text->textType == WS_XML_TEXT_TYPE_UTF8) text_utf8 = (const WS_XML_UTF8_TEXT *)text;
+        else
+        {
+            if ((hr = text_to_utf8text( text, NULL, NULL, &new )) != S_OK) return hr;
+            text_utf8 = new;
+        }
+        len = text_utf8->value.length;
+        if ((hr = write_grow_buffer( writer, sizeof(len) + len )) != S_OK)
+        {
+            heap_free( new );
+            return hr;
+        }
+        write_bytes( writer, (const BYTE *)&len, sizeof(len) );
+        write_bytes( writer, text_utf8->value.bytes, len );
+        heap_free( new );
+        return S_OK;
+    }
+    case RECORD_BYTES8_TEXT:
+    {
+        WS_XML_BASE64_TEXT *text_base64 = (WS_XML_BASE64_TEXT *)text;
+        if ((hr = write_grow_buffer( writer, 1 + text_base64->length )) != S_OK) return hr;
+        write_char( writer, text_base64->length );
+        write_bytes( writer, text_base64->bytes, text_base64->length );
+        return S_OK;
+    }
+    case RECORD_BYTES16_TEXT:
+    {
+        WS_XML_BASE64_TEXT *text_base64 = (WS_XML_BASE64_TEXT *)text;
+        UINT16 len = text_base64->length;
+        if ((hr = write_grow_buffer( writer, sizeof(len) + len )) != S_OK) return hr;
+        write_bytes( writer, (const BYTE *)&len, sizeof(len) );
+        write_bytes( writer, text_base64->bytes, len );
+        return S_OK;
+    }
+    case RECORD_ZERO_TEXT:
+    case RECORD_ONE_TEXT:
+    case RECORD_FALSE_TEXT:
+    case RECORD_TRUE_TEXT:
+        return S_OK;
+
+    case RECORD_INT8_TEXT:
+    {
+        INT8 val = get_text_value_int( text );
+        if ((hr = write_grow_buffer( writer, sizeof(val) )) != S_OK) return hr;
+        write_char( writer, val );
+        return S_OK;
+    }
+    case RECORD_INT16_TEXT:
+    {
+        INT16 val = get_text_value_int( text );
+        if ((hr = write_grow_buffer( writer, sizeof(val) )) != S_OK) return hr;
+        write_bytes( writer, (const BYTE *)&val, sizeof(val) );
+        return S_OK;
+    }
+    case RECORD_INT32_TEXT:
+    {
+        INT32 val = get_text_value_int( text );
+        if ((hr = write_grow_buffer( writer, sizeof(val) )) != S_OK) return hr;
+        write_bytes( writer, (const BYTE *)&val, sizeof(val) );
+        return S_OK;
+    }
+    case RECORD_INT64_TEXT:
+    {
+        INT64 val = get_text_value_int( text );
+        if ((hr = write_grow_buffer( writer, sizeof(val) )) != S_OK) return hr;
+        write_bytes( writer, (const BYTE *)&val, sizeof(val) );
+        return S_OK;
+    }
+    case RECORD_UINT64_TEXT:
+    {
+        WS_XML_UINT64_TEXT *text_uint64 = (WS_XML_UINT64_TEXT *)text;
+        if ((hr = write_grow_buffer( writer, sizeof(text_uint64->value) )) != S_OK) return hr;
+        write_bytes( writer, (const BYTE *)&text_uint64->value, sizeof(text_uint64->value) );
+        return S_OK;
+    }
+    case RECORD_DOUBLE_TEXT:
+    {
+        WS_XML_DOUBLE_TEXT *text_double = (WS_XML_DOUBLE_TEXT *)text;
+        if ((hr = write_grow_buffer( writer, sizeof(text_double->value) )) != S_OK) return hr;
+        write_bytes( writer, (const BYTE *)&text_double->value, sizeof(text_double->value) );
+        return S_OK;
+    }
+    case RECORD_GUID_TEXT:
+    {
+        WS_XML_GUID_TEXT *text_guid = (WS_XML_GUID_TEXT *)text;
+        if ((hr = write_grow_buffer( writer, sizeof(text_guid->value) )) != S_OK) return hr;
+        write_bytes( writer, (const BYTE *)&text_guid->value, sizeof(text_guid->value) );
+        return S_OK;
+    }
+    case RECORD_UNIQUE_ID_TEXT:
+    {
+        WS_XML_UNIQUE_ID_TEXT *text_unique_id = (WS_XML_UNIQUE_ID_TEXT *)text;
+        if ((hr = write_grow_buffer( writer, sizeof(text_unique_id->value) )) != S_OK) return hr;
+        write_bytes( writer, (const BYTE *)&text_unique_id->value, sizeof(text_unique_id->value) );
+        return S_OK;
+    }
+    case RECORD_DATETIME_TEXT:
+    {
+        WS_XML_DATETIME_TEXT *text_datetime = (WS_XML_DATETIME_TEXT *)text;
+        UINT64 val = text_datetime->value.ticks;
+
+        assert( val <= TICKS_MAX );
+        if (text_datetime->value.format == WS_DATETIME_FORMAT_UTC) val |= (UINT64)1 << 62;
+        else if (text_datetime->value.format == WS_DATETIME_FORMAT_LOCAL) val |= (UINT64)1 << 63;
+
+        if ((hr = write_grow_buffer( writer, sizeof(val) )) != S_OK) return hr;
+        write_bytes( writer, (const BYTE *)&val, sizeof(val) );
+        return S_OK;
+    }
+    default:
+        FIXME( "unhandled record type %02x\n", type );
+        return E_NOTIMPL;
+    }
+}
+
+static enum record_type get_attr_record_type( const WS_XML_ATTRIBUTE *attr, BOOL use_dict )
+{
+    if (!attr->prefix || !attr->prefix->length)
+    {
+        if (use_dict) return RECORD_SHORT_DICTIONARY_ATTRIBUTE;
+        return RECORD_SHORT_ATTRIBUTE;
+    }
     if (attr->prefix->length == 1 && attr->prefix->bytes[0] >= 'a' && attr->prefix->bytes[0] <= 'z')
     {
         if (use_dict) return RECORD_PREFIX_DICTIONARY_ATTRIBUTE_A + attr->prefix->bytes[0] - 'a';
@@ -1584,236 +2006,15 @@ static HRESULT write_add_attribute( struct writer *writer, const WS_XML_STRING *
 /**************************************************************************
  *          WsWriteStartAttribute		[webservices.@]
  */
-HRESULT WINAPI WsWriteStartAttribute( WS_XML_WRITER *handle, const WS_XML_STRING *prefix,
-                                      const WS_XML_STRING *localname, const WS_XML_STRING *ns,
-                                      BOOL single, WS_ERROR *error )
-{
-    struct writer *writer = (struct writer *)handle;
-    HRESULT hr;
-
-    TRACE( "%p %s %s %s %d %p\n", handle, debugstr_xmlstr(prefix), debugstr_xmlstr(localname),
-           debugstr_xmlstr(ns), single, error );
-    if (error) FIXME( "ignoring error parameter\n" );
-
-    if (!writer || !localname || !ns) return E_INVALIDARG;
-
-    EnterCriticalSection( &writer->cs );
-
-    if (writer->magic != WRITER_MAGIC)
-    {
-        LeaveCriticalSection( &writer->cs );
-        return E_INVALIDARG;
-    }
-
-    if (writer->state != WRITER_STATE_STARTELEMENT)
-    {
-        LeaveCriticalSection( &writer->cs );
-        return WS_E_INVALID_OPERATION;
-    }
-
-    if ((hr = write_add_attribute( writer, prefix, localname, ns, single )) == S_OK)
-        writer->state = WRITER_STATE_STARTATTRIBUTE;
-
-    LeaveCriticalSection( &writer->cs );
-    return hr;
-}
-
-/* flush current start element if necessary */
-static HRESULT write_flush( struct writer *writer )
-{
-    if (writer->state == WRITER_STATE_STARTELEMENT)
-    {
-        HRESULT hr;
-        if ((hr = set_namespaces( writer )) != S_OK) return hr;
-        if ((hr = write_startelement( writer )) != S_OK) return hr;
-        if ((hr = write_endstartelement( writer )) != S_OK) return hr;
-        writer->state = WRITER_STATE_ENDSTARTELEMENT;
-    }
-    return S_OK;
-}
-
-static HRESULT write_add_cdata_node( struct writer *writer )
-{
-    struct node *node, *parent;
-    if (!(parent = find_parent( writer ))) return WS_E_INVALID_FORMAT;
-    if (!(node = alloc_node( WS_XML_NODE_TYPE_CDATA ))) return E_OUTOFMEMORY;
-    write_insert_node( writer, parent, node );
-    return S_OK;
-}
-
-static HRESULT write_add_endcdata_node( struct writer *writer )
-{
-    struct node *node;
-    if (!(node = alloc_node( WS_XML_NODE_TYPE_END_CDATA ))) return E_OUTOFMEMORY;
-    node->parent = writer->current;
-    list_add_tail( &node->parent->children, &node->entry );
-    return S_OK;
-}
-
-static HRESULT write_cdata( struct writer *writer )
-{
-    HRESULT hr;
-    if ((hr = write_grow_buffer( writer, 9 )) != S_OK) return hr;
-    write_bytes( writer, (const BYTE *)"<![CDATA[", 9 );
-    return S_OK;
-}
-
-static HRESULT write_cdata_node( struct writer *writer )
-{
-    HRESULT hr;
-    if ((hr = write_flush( 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;
-    writer->state = WRITER_STATE_STARTCDATA;
-    return S_OK;
-}
-
-/**************************************************************************
- *          WsWriteStartCData		[webservices.@]
- */
-HRESULT WINAPI WsWriteStartCData( WS_XML_WRITER *handle, WS_ERROR *error )
-{
-    struct writer *writer = (struct writer *)handle;
-    HRESULT hr;
-
-    TRACE( "%p %p\n", handle, error );
-    if (error) FIXME( "ignoring error parameter\n" );
-
-    if (!writer) return E_INVALIDARG;
-
-    EnterCriticalSection( &writer->cs );
-
-    if (writer->magic != WRITER_MAGIC)
-    {
-        LeaveCriticalSection( &writer->cs );
-        return E_INVALIDARG;
-    }
-
-    hr = write_cdata_node( writer );
-
-    LeaveCriticalSection( &writer->cs );
-    return hr;
-}
-
-static HRESULT write_endcdata( struct writer *writer )
-{
-    HRESULT hr;
-    if ((hr = write_grow_buffer( writer, 3 )) != S_OK) return hr;
-    write_bytes( writer, (const BYTE *)"]]>", 3 );
-    return S_OK;
-}
-
-static HRESULT write_endcdata_node( struct writer *writer )
-{
-    HRESULT hr;
-    if ((hr = write_endcdata( writer )) != S_OK) return hr;
-    writer->current = writer->current->parent;
-    writer->state = WRITER_STATE_ENDCDATA;
-    return S_OK;
-}
-
-/**************************************************************************
- *          WsWriteEndCData		[webservices.@]
- */
-HRESULT WINAPI WsWriteEndCData( WS_XML_WRITER *handle, WS_ERROR *error )
-{
-    struct writer *writer = (struct writer *)handle;
-    HRESULT hr;
-
-    TRACE( "%p %p\n", handle, error );
-    if (error) FIXME( "ignoring error parameter\n" );
-
-    if (!writer) return E_INVALIDARG;
-
-    EnterCriticalSection( &writer->cs );
-
-    if (writer->magic != WRITER_MAGIC)
-    {
-        LeaveCriticalSection( &writer->cs );
-        return E_INVALIDARG;
-    }
-
-    if (writer->state != WRITER_STATE_TEXT)
-    {
-        LeaveCriticalSection( &writer->cs );
-        return WS_E_INVALID_OPERATION;
-    }
-
-    hr = write_endcdata_node( writer );
-
-    LeaveCriticalSection( &writer->cs );
-    return hr;
-}
-
-static HRESULT write_add_element_node( struct writer *writer, const WS_XML_STRING *prefix,
-                                       const WS_XML_STRING *localname, const WS_XML_STRING *ns )
-{
-    struct node *node, *parent;
-    WS_XML_ELEMENT_NODE *elem;
-
-    if (!(parent = find_parent( writer ))) return WS_E_INVALID_FORMAT;
-
-    if (!prefix && node_type( parent ) == WS_XML_NODE_TYPE_ELEMENT)
-    {
-        elem = &parent->hdr;
-        if (WsXmlStringEquals( ns, elem->ns, NULL ) == S_OK) prefix = elem->prefix;
-    }
-
-    if (!(node = alloc_node( WS_XML_NODE_TYPE_ELEMENT ))) return E_OUTOFMEMORY;
-    elem = &node->hdr;
-
-    if (prefix && !(elem->prefix = dup_xml_string( prefix, writer->dict_do_lookup )))
-    {
-        free_node( node );
-        return E_OUTOFMEMORY;
-    }
-    if (!(elem->localName = dup_xml_string( localname, writer->dict_do_lookup )))
-    {
-        free_node( node );
-        return E_OUTOFMEMORY;
-    }
-    if (!(elem->ns = dup_xml_string( ns, writer->dict_do_lookup )))
-    {
-        free_node( node );
-        return E_OUTOFMEMORY;
-    }
-    write_insert_node( writer, parent, node );
-    return S_OK;
-}
-
-static HRESULT write_add_endelement_node( struct writer *writer, struct node *parent )
-{
-    struct node *node;
-    if (!(node = alloc_node( WS_XML_NODE_TYPE_END_ELEMENT ))) return E_OUTOFMEMORY;
-    node->parent = parent;
-    list_add_tail( &parent->children, &node->entry );
-    return S_OK;
-}
-
-static HRESULT write_element_node( struct writer *writer, const WS_XML_STRING *prefix,
-                                   const WS_XML_STRING *localname, const WS_XML_STRING *ns )
-{
-    HRESULT hr;
-    if ((hr = write_flush( 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;
-    return S_OK;
-}
-
-/**************************************************************************
- *          WsWriteStartElement		[webservices.@]
- */
-HRESULT WINAPI WsWriteStartElement( WS_XML_WRITER *handle, const WS_XML_STRING *prefix,
-                                    const WS_XML_STRING *localname, const WS_XML_STRING *ns,
-                                    WS_ERROR *error )
+HRESULT WINAPI WsWriteStartAttribute( WS_XML_WRITER *handle, const WS_XML_STRING *prefix,
+                                      const WS_XML_STRING *localname, const WS_XML_STRING *ns,
+                                      BOOL single, WS_ERROR *error )
 {
     struct writer *writer = (struct writer *)handle;
     HRESULT hr;
 
-    TRACE( "%p %s %s %s %p\n", handle, debugstr_xmlstr(prefix), debugstr_xmlstr(localname),
-           debugstr_xmlstr(ns), error );
+    TRACE( "%p %s %s %s %d %p\n", handle, debugstr_xmlstr(prefix), debugstr_xmlstr(localname),
+           debugstr_xmlstr(ns), single, error );
     if (error) FIXME( "ignoring error parameter\n" );
 
     if (!writer || !localname || !ns) return E_INVALIDARG;
@@ -1826,394 +2027,231 @@ HRESULT WINAPI WsWriteStartElement( WS_XML_WRITER *handle, const WS_XML_STRING *
         return E_INVALIDARG;
     }
 
-    hr = write_element_node( writer, prefix, localname, ns );
+    if (writer->state != WRITER_STATE_STARTELEMENT)
+    {
+        LeaveCriticalSection( &writer->cs );
+        return WS_E_INVALID_OPERATION;
+    }
+
+    if ((hr = write_add_attribute( writer, prefix, localname, ns, single )) == S_OK)
+        writer->state = WRITER_STATE_STARTATTRIBUTE;
 
     LeaveCriticalSection( &writer->cs );
     return hr;
 }
 
-static ULONG format_bool( const BOOL *ptr, unsigned char *buf )
+/* flush current start element if necessary */
+static HRESULT write_flush( struct writer *writer )
 {
-    static const unsigned char bool_true[] = {'t','r','u','e'}, bool_false[] = {'f','a','l','s','e'};
-    if (*ptr)
+    if (writer->state == WRITER_STATE_STARTELEMENT)
     {
-        memcpy( buf, bool_true, sizeof(bool_true) );
-        return sizeof(bool_true);
+        HRESULT hr;
+        if ((hr = set_namespaces( writer )) != S_OK) return hr;
+        if ((hr = write_startelement( writer )) != S_OK) return hr;
+        if ((hr = write_endstartelement( writer )) != S_OK) return hr;
+        writer->state = WRITER_STATE_ENDSTARTELEMENT;
     }
-    memcpy( buf, bool_false, sizeof(bool_false) );
-    return sizeof(bool_false);
+    return S_OK;
 }
 
-static ULONG format_int32( const INT32 *ptr, unsigned char *buf )
+static HRESULT write_add_cdata_node( struct writer *writer )
 {
-    return wsprintfA( (char *)buf, "%d", *ptr );
+    struct node *node, *parent;
+    if (!(parent = find_parent( writer ))) return WS_E_INVALID_FORMAT;
+    if (!(node = alloc_node( WS_XML_NODE_TYPE_CDATA ))) return E_OUTOFMEMORY;
+    write_insert_node( writer, parent, node );
+    return S_OK;
 }
 
-static ULONG format_int64( const INT64 *ptr, unsigned char *buf )
+static HRESULT write_add_endcdata_node( struct writer *writer )
 {
-    return wsprintfA( (char *)buf, "%I64d", *ptr );
+    struct node *node;
+    if (!(node = alloc_node( WS_XML_NODE_TYPE_END_CDATA ))) return E_OUTOFMEMORY;
+    node->parent = writer->current;
+    list_add_tail( &node->parent->children, &node->entry );
+    return S_OK;
 }
 
-static ULONG format_uint64( const UINT64 *ptr, unsigned char *buf )
+static HRESULT write_cdata( struct writer *writer )
 {
-    return wsprintfA( (char *)buf, "%I64u", *ptr );
+    HRESULT hr;
+    if ((hr = write_grow_buffer( writer, 9 )) != S_OK) return hr;
+    write_bytes( writer, (const BYTE *)"<![CDATA[", 9 );
+    return S_OK;
 }
 
-static ULONG format_double( const double *ptr, unsigned char *buf )
+static HRESULT write_cdata_node( struct writer *writer )
 {
-#ifdef HAVE_POWL
-    static const long double precision = 0.0000000000000001;
-    unsigned char *p = buf;
-    long double val = *ptr;
-    int neg, mag, mag2, use_exp;
+    HRESULT hr;
+    if ((hr = write_flush( 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;
+    writer->state = WRITER_STATE_STARTCDATA;
+    return S_OK;
+}
 
-    if (isnan( val ))
-    {
-        memcpy( buf, "NaN", 3 );
-        return 3;
-    }
-    if (isinf( val ))
-    {
-        if (val < 0)
-        {
-            memcpy( buf, "-INF", 4 );
-            return 4;
-        }
-        memcpy( buf, "INF", 3 );
-        return 3;
-    }
-    if (val == 0.0)
-    {
-        *p = '0';
-        return 1;
-    }
+/**************************************************************************
+ *          WsWriteStartCData		[webservices.@]
+ */
+HRESULT WINAPI WsWriteStartCData( WS_XML_WRITER *handle, WS_ERROR *error )
+{
+    struct writer *writer = (struct writer *)handle;
+    HRESULT hr;
 
-    if ((neg = val < 0))
-    {
-        *p++ = '-';
-        val = -val;
-    }
+    TRACE( "%p %p\n", handle, error );
+    if (error) FIXME( "ignoring error parameter\n" );
 
-    mag = log10l( val );
-    use_exp = (mag >= 15 || (neg && mag >= 1) || mag <= -1);
-    if (use_exp)
-    {
-        if (mag < 0) mag -= 1;
-        val = val / powl( 10.0, mag );
-        mag2 = mag;
-        mag = 0;
-    }
-    else if (mag < 1) mag = 0;
+    if (!writer) return E_INVALIDARG;
 
-    while (val > precision || mag >= 0)
-    {
-        long double weight = powl( 10.0, mag );
-        if (weight > 0 && !isinf( weight ))
-        {
-            int digit = floorl( val / weight );
-            val -= digit * weight;
-            *(p++) = '0' + digit;
-        }
-        if (!mag && val > precision) *(p++) = '.';
-        mag--;
-    }
+    EnterCriticalSection( &writer->cs );
 
-    if (use_exp)
+    if (writer->magic != WRITER_MAGIC)
     {
-        int i, j;
-        *(p++) = 'E';
-        if (mag2 > 0) *(p++) = '+';
-        else
-        {
-            *(p++) = '-';
-            mag2 = -mag2;
-        }
-        mag = 0;
-        while (mag2 > 0)
-        {
-            *(p++) = '0' + mag2 % 10;
-            mag2 /= 10;
-            mag++;
-        }
-        for (i = -mag, j = -1; i < j; i++, j--)
-        {
-            p[i] ^= p[j];
-            p[j] ^= p[i];
-            p[i] ^= p[j];
-        }
+        LeaveCriticalSection( &writer->cs );
+        return E_INVALIDARG;
     }
 
-    return p - buf;
-#else
-    FIXME( "powl not found at build time\n" );
-    return 0;
-#endif
-}
+    hr = write_cdata_node( writer );
 
-static inline int year_size( int year )
-{
-    return leap_year( year ) ? 366 : 365;
+    LeaveCriticalSection( &writer->cs );
+    return hr;
 }
 
-#define TZ_OFFSET 8
-static ULONG format_datetime( const WS_DATETIME *ptr, unsigned char *buf )
+static HRESULT write_endcdata( struct writer *writer )
 {
-    static const char fmt[] = "%04u-%02u-%02uT%02u:%02u:%02u";
-    int day, hour, min, sec, sec_frac, month = 0, year = 1, tz_hour;
-    unsigned __int64 ticks, day_ticks;
-    ULONG len;
-
-    if (ptr->format == WS_DATETIME_FORMAT_LOCAL &&
-        ptr->ticks >= TICKS_1601_01_01 + TZ_OFFSET * TICKS_PER_HOUR)
-    {
-        ticks = ptr->ticks - TZ_OFFSET * TICKS_PER_HOUR;
-        tz_hour = TZ_OFFSET;
-    }
-    else
-    {
-        ticks = ptr->ticks;
-        tz_hour = 0;
-    }
-    day = ticks / TICKS_PER_DAY;
-    day_ticks = ticks % TICKS_PER_DAY;
-    hour = day_ticks / TICKS_PER_HOUR;
-    min = (day_ticks % TICKS_PER_HOUR) / TICKS_PER_MIN;
-    sec = (day_ticks % TICKS_PER_MIN) / TICKS_PER_SEC;
-    sec_frac = day_ticks % TICKS_PER_SEC;
-
-    while (day >= year_size( year ))
-    {
-        day -= year_size( year );
-        year++;
-    }
-    while (day >= month_days[leap_year( year )][month])
-    {
-        day -= month_days[leap_year( year )][month];
-        month++;
-    }
-
-    len = sprintf( (char *)buf, fmt, year, month + 1, day + 1, hour, min, sec );
-    if (sec_frac)
-    {
-        static const char fmt_frac[] = ".%07u";
-        len += sprintf( (char *)buf + len, fmt_frac, sec_frac );
-        while (buf[len - 1] == '0') len--;
-    }
-    if (ptr->format == WS_DATETIME_FORMAT_UTC)
-    {
-        buf[len++] = 'Z';
-    }
-    else if (ptr->format == WS_DATETIME_FORMAT_LOCAL)
-    {
-        static const char fmt_tz[] = "%c%02u:00";
-        len += sprintf( (char *)buf + len, fmt_tz, tz_hour ? '-' : '+', tz_hour );
-    }
-
-    return len;
+    HRESULT hr;
+    if ((hr = write_grow_buffer( writer, 3 )) != S_OK) return hr;
+    write_bytes( writer, (const BYTE *)"]]>", 3 );
+    return S_OK;
 }
 
-static ULONG format_guid( const GUID *ptr, unsigned char *buf )
+static HRESULT write_endcdata_node( struct writer *writer )
 {
-    static const char fmt[] = "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x";
-    return sprintf( (char *)buf, fmt, ptr->Data1, ptr->Data2, ptr->Data3,
-                    ptr->Data4[0], ptr->Data4[1], ptr->Data4[2], ptr->Data4[3],
-                    ptr->Data4[4], ptr->Data4[5], ptr->Data4[6], ptr->Data4[7] );
+    HRESULT hr;
+    if ((hr = write_endcdata( writer )) != S_OK) return hr;
+    writer->current = writer->current->parent;
+    writer->state = WRITER_STATE_ENDCDATA;
+    return S_OK;
 }
 
-static ULONG format_urn( const GUID *ptr, unsigned char *buf )
+/**************************************************************************
+ *          WsWriteEndCData		[webservices.@]
+ */
+HRESULT WINAPI WsWriteEndCData( WS_XML_WRITER *handle, WS_ERROR *error )
 {
-    static const char fmt[] = "urn:uuid:%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x";
-    return sprintf( (char *)buf, fmt, ptr->Data1, ptr->Data2, ptr->Data3,
-                    ptr->Data4[0], ptr->Data4[1], ptr->Data4[2], ptr->Data4[3],
-                    ptr->Data4[4], ptr->Data4[5], ptr->Data4[6], ptr->Data4[7] );
-}
+    struct writer *writer = (struct writer *)handle;
+    HRESULT hr;
+
+    TRACE( "%p %p\n", handle, error );
+    if (error) FIXME( "ignoring error parameter\n" );
+
+    if (!writer) return E_INVALIDARG;
+
+    EnterCriticalSection( &writer->cs );
 
-static ULONG format_qname( const WS_XML_STRING *prefix, const WS_XML_STRING *localname, unsigned char *buf )
-{
-    ULONG len = 0;
-    if (prefix && prefix->length)
+    if (writer->magic != WRITER_MAGIC)
     {
-        memcpy( buf, prefix->bytes, prefix->length );
-        len += prefix->length;
-        buf[len++] = ':';
+        LeaveCriticalSection( &writer->cs );
+        return E_INVALIDARG;
     }
-    memcpy( buf + len, localname->bytes, localname->length );
-    return len + localname->length;
-}
-
-static ULONG encode_base64( const unsigned char *bin, ULONG len, unsigned char *buf )
-{
-    static const char base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
-    ULONG i = 0, x;
 
-    while (len > 0)
+    if (writer->state != WRITER_STATE_TEXT)
     {
-        buf[i++] = base64[(bin[0] & 0xfc) >> 2];
-        x = (bin[0] & 3) << 4;
-        if (len == 1)
-        {
-            buf[i++] = base64[x];
-            buf[i++] = '=';
-            buf[i++] = '=';
-            break;
-        }
-        buf[i++] = base64[x | ((bin[1] & 0xf0) >> 4)];
-        x = (bin[1] & 0x0f) << 2;
-        if (len == 2)
-        {
-            buf[i++] = base64[x];
-            buf[i++] = '=';
-            break;
-        }
-        buf[i++] = base64[x | ((bin[2] & 0xc0) >> 6)];
-        buf[i++] = base64[bin[2] & 0x3f];
-        bin += 3;
-        len -= 3;
+        LeaveCriticalSection( &writer->cs );
+        return WS_E_INVALID_OPERATION;
     }
-    return i;
+
+    hr = write_endcdata_node( writer );
+
+    LeaveCriticalSection( &writer->cs );
+    return hr;
 }
 
-static HRESULT text_to_utf8text( const WS_XML_TEXT *text, const WS_XML_UTF8_TEXT *old, ULONG *offset,
-                                 WS_XML_UTF8_TEXT **ret )
+static HRESULT write_add_element_node( struct writer *writer, const WS_XML_STRING *prefix,
+                                       const WS_XML_STRING *localname, const WS_XML_STRING *ns )
 {
-    ULONG len_old = old ? old->value.length : 0;
-    if (offset) *offset = len_old;
+    struct node *node, *parent;
+    WS_XML_ELEMENT_NODE *elem;
 
-    switch (text->textType)
-    {
-    case WS_XML_TEXT_TYPE_UTF8:
-    {
-        const WS_XML_UTF8_TEXT *src = (const WS_XML_UTF8_TEXT *)text;
+    if (!(parent = find_parent( writer ))) return WS_E_INVALID_FORMAT;
 
-        if (!(*ret = alloc_utf8_text( NULL, len_old + src->value.length ))) return E_OUTOFMEMORY;
-        if (old) memcpy( (*ret)->value.bytes, old->value.bytes, len_old );
-        memcpy( (*ret)->value.bytes + len_old, src->value.bytes, src->value.length );
-        return S_OK;
-    }
-    case WS_XML_TEXT_TYPE_UTF16:
+    if (!prefix && node_type( parent ) == WS_XML_NODE_TYPE_ELEMENT)
     {
-        const WS_XML_UTF16_TEXT *src = (const WS_XML_UTF16_TEXT *)text;
-        const WCHAR *str = (const WCHAR *)src->bytes;
-        ULONG len = src->byteCount / sizeof(WCHAR), len_utf8;
-
-        if (src->byteCount % sizeof(WCHAR)) return E_INVALIDARG;
-        len_utf8 = WideCharToMultiByte( CP_UTF8, 0, str, len, NULL, 0, NULL, NULL );
-        if (!(*ret = alloc_utf8_text( NULL, len_old + len_utf8 ))) return E_OUTOFMEMORY;
-        if (old) memcpy( (*ret)->value.bytes, old->value.bytes, len_old );
-        WideCharToMultiByte( CP_UTF8, 0, str, len, (char *)(*ret)->value.bytes + len_old, len_utf8, NULL, NULL );
-        return S_OK;
+        elem = &parent->hdr;
+        if (WsXmlStringEquals( ns, elem->ns, NULL ) == S_OK) prefix = elem->prefix;
     }
-    case WS_XML_TEXT_TYPE_BASE64:
-    {
-        const WS_XML_BASE64_TEXT *base64 = (const WS_XML_BASE64_TEXT *)text;
-        ULONG len = ((4 * base64->length / 3) + 3) & ~3;
 
-        if (!(*ret = alloc_utf8_text( NULL, len_old + len ))) return E_OUTOFMEMORY;
-        if (old) memcpy( (*ret)->value.bytes, old->value.bytes, len_old );
-        (*ret)->value.length = encode_base64( base64->bytes, base64->length, (*ret)->value.bytes + len_old ) + len_old;
-        return S_OK;
-    }
-    case WS_XML_TEXT_TYPE_BOOL:
-    {
-        const WS_XML_BOOL_TEXT *bool_text = (const WS_XML_BOOL_TEXT *)text;
+    if (!(node = alloc_node( WS_XML_NODE_TYPE_ELEMENT ))) return E_OUTOFMEMORY;
+    elem = &node->hdr;
 
-        if (!(*ret = alloc_utf8_text( NULL, len_old + 5 ))) return E_OUTOFMEMORY;
-        if (old) memcpy( (*ret)->value.bytes, old->value.bytes, len_old );
-        (*ret)->value.length = format_bool( &bool_text->value, (*ret)->value.bytes + len_old ) + len_old;
-        return S_OK;
-    }
-    case WS_XML_TEXT_TYPE_INT32:
+    if (prefix && !(elem->prefix = dup_xml_string( prefix, writer->dict_do_lookup )))
     {
-        const WS_XML_INT32_TEXT *int32_text = (const WS_XML_INT32_TEXT *)text;
-        unsigned char buf[12]; /* "-2147483648" */
-        ULONG len = format_int32( &int32_text->value, buf );
-
-        if (!(*ret = alloc_utf8_text( NULL, len_old + len ))) return E_OUTOFMEMORY;
-        if (old) memcpy( (*ret)->value.bytes, old->value.bytes, len_old );
-        memcpy( (*ret)->value.bytes + len_old, buf, len );
-        return S_OK;
+        free_node( node );
+        return E_OUTOFMEMORY;
     }
-    case WS_XML_TEXT_TYPE_INT64:
+    if (!(elem->localName = dup_xml_string( localname, writer->dict_do_lookup )))
     {
-        const WS_XML_INT64_TEXT *int64_text = (const WS_XML_INT64_TEXT *)text;
-        unsigned char buf[21]; /* "-9223372036854775808" */
-        ULONG len = format_int64( &int64_text->value, buf );
-
-        if (!(*ret = alloc_utf8_text( NULL, len_old + len ))) return E_OUTOFMEMORY;
-        if (old) memcpy( (*ret)->value.bytes, old->value.bytes, len_old );
-        memcpy( (*ret)->value.bytes + len_old, buf, len );
-        return S_OK;
+        free_node( node );
+        return E_OUTOFMEMORY;
     }
-    case WS_XML_TEXT_TYPE_UINT64:
+    if (!(elem->ns = dup_xml_string( ns, writer->dict_do_lookup )))
     {
-        const WS_XML_UINT64_TEXT *uint64_text = (const WS_XML_UINT64_TEXT *)text;
-        unsigned char buf[21]; /* "18446744073709551615" */
-        ULONG len = format_uint64( &uint64_text->value, buf );
-
-        if (!(*ret = alloc_utf8_text( NULL, len_old + len ))) return E_OUTOFMEMORY;
-        if (old) memcpy( (*ret)->value.bytes, old->value.bytes, len_old );
-        memcpy( (*ret)->value.bytes + len_old, buf, len );
-        return S_OK;
+        free_node( node );
+        return E_OUTOFMEMORY;
     }
-    case WS_XML_TEXT_TYPE_DOUBLE:
-    {
-        const WS_XML_DOUBLE_TEXT *double_text = (const WS_XML_DOUBLE_TEXT *)text;
-        unsigned char buf[32]; /* "-1.1111111111111111E-308", oversized to address Valgrind limitations */
-        unsigned short fpword;
-        ULONG len;
+    write_insert_node( writer, parent, node );
+    return S_OK;
+}
 
-        if (!set_fpword( 0x37f, &fpword )) return E_NOTIMPL;
-        len = format_double( &double_text->value, buf );
-        restore_fpword( fpword );
-        if (!len) return E_NOTIMPL;
+static HRESULT write_add_endelement_node( struct writer *writer, struct node *parent )
+{
+    struct node *node;
+    if (!(node = alloc_node( WS_XML_NODE_TYPE_END_ELEMENT ))) return E_OUTOFMEMORY;
+    node->parent = parent;
+    list_add_tail( &parent->children, &node->entry );
+    return S_OK;
+}
 
-        if (!(*ret = alloc_utf8_text( NULL, len_old + len ))) return E_OUTOFMEMORY;
-        if (old) memcpy( (*ret)->value.bytes, old->value.bytes, len_old );
-        memcpy( (*ret)->value.bytes + len_old, buf, len );
-        return S_OK;
-    }
-    case WS_XML_TEXT_TYPE_GUID:
-    {
-        const WS_XML_GUID_TEXT *id = (const WS_XML_GUID_TEXT *)text;
+static HRESULT write_element_node( struct writer *writer, const WS_XML_STRING *prefix,
+                                   const WS_XML_STRING *localname, const WS_XML_STRING *ns )
+{
+    HRESULT hr;
+    if ((hr = write_flush( 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;
+    return S_OK;
+}
 
-        if (!(*ret = alloc_utf8_text( NULL, len_old + 37 ))) return E_OUTOFMEMORY;
-        if (old) memcpy( (*ret)->value.bytes, old->value.bytes, len_old );
-        (*ret)->value.length = format_guid( &id->value, (*ret)->value.bytes + len_old ) + len_old;
-        return S_OK;
-    }
-    case WS_XML_TEXT_TYPE_UNIQUE_ID:
-    {
-        const WS_XML_UNIQUE_ID_TEXT *id = (const WS_XML_UNIQUE_ID_TEXT *)text;
+/**************************************************************************
+ *          WsWriteStartElement		[webservices.@]
+ */
+HRESULT WINAPI WsWriteStartElement( WS_XML_WRITER *handle, const WS_XML_STRING *prefix,
+                                    const WS_XML_STRING *localname, const WS_XML_STRING *ns,
+                                    WS_ERROR *error )
+{
+    struct writer *writer = (struct writer *)handle;
+    HRESULT hr;
 
-        if (!(*ret = alloc_utf8_text( NULL, len_old + 46 ))) return E_OUTOFMEMORY;
-        if (old) memcpy( (*ret)->value.bytes, old->value.bytes, len_old );
-        (*ret)->value.length = format_urn( &id->value, (*ret)->value.bytes + len_old ) + len_old;
-        return S_OK;
-    }
-    case WS_XML_TEXT_TYPE_DATETIME:
-    {
-        const WS_XML_DATETIME_TEXT *dt = (const WS_XML_DATETIME_TEXT *)text;
+    TRACE( "%p %s %s %s %p\n", handle, debugstr_xmlstr(prefix), debugstr_xmlstr(localname),
+           debugstr_xmlstr(ns), error );
+    if (error) FIXME( "ignoring error parameter\n" );
 
-        if (!(*ret = alloc_utf8_text( NULL, len_old + 34 ))) return E_OUTOFMEMORY;
-        if (old) memcpy( (*ret)->value.bytes, old->value.bytes, len_old );
-        (*ret)->value.length = format_datetime( &dt->value, (*ret)->value.bytes + len_old ) + len_old;
-        return S_OK;
-    }
-    case WS_XML_TEXT_TYPE_QNAME:
-    {
-        const WS_XML_QNAME_TEXT *qn = (const WS_XML_QNAME_TEXT *)text;
-        ULONG len = qn->localName->length;
+    if (!writer || !localname || !ns) return E_INVALIDARG;
 
-        if (qn->prefix && qn->prefix->length) len += qn->prefix->length + 1;
-        if (!(*ret = alloc_utf8_text( NULL, len_old + len ))) return E_OUTOFMEMORY;
-        if (old) memcpy( (*ret)->value.bytes, old->value.bytes, len_old );
-        (*ret)->value.length = format_qname( qn->prefix, qn->localName, (*ret)->value.bytes + len_old ) + len_old;
-        return S_OK;
-    }
-    default:
-        FIXME( "unhandled text type %u\n", text->textType );
-        return E_NOTIMPL;
+    EnterCriticalSection( &writer->cs );
+
+    if (writer->magic != WRITER_MAGIC)
+    {
+        LeaveCriticalSection( &writer->cs );
+        return E_INVALIDARG;
     }
+
+    hr = write_element_node( writer, prefix, localname, ns );
+
+    LeaveCriticalSection( &writer->cs );
+    return hr;
 }
 
 static HRESULT text_to_text( const WS_XML_TEXT *text, const WS_XML_TEXT *old, ULONG *offset, WS_XML_TEXT **ret )
@@ -2238,16 +2276,14 @@ static HRESULT text_to_text( const WS_XML_TEXT *text, const WS_XML_TEXT *old, UL
     case WS_XML_TEXT_TYPE_UTF16:
     {
         const WS_XML_UTF16_TEXT *utf16 = (const WS_XML_UTF16_TEXT *)text;
-        const WS_XML_UTF8_TEXT *utf8_old = (const WS_XML_UTF8_TEXT *)old;
-        WS_XML_UTF8_TEXT *new;
-        const WCHAR *str = (const WCHAR *)utf16->bytes;
-        ULONG len = utf16->byteCount / sizeof(WCHAR), len_utf8, len_old = utf8_old ? utf8_old->value.length : 0;
+        const WS_XML_UTF16_TEXT *utf16_old = (const WS_XML_UTF16_TEXT *)old;
+        WS_XML_UTF16_TEXT *new;
+        ULONG len = utf16->byteCount, len_old = utf16_old ? utf16_old->byteCount : 0;
 
         if (utf16->byteCount % sizeof(WCHAR)) return E_INVALIDARG;
-        len_utf8 = WideCharToMultiByte( CP_UTF8, 0, str, len, NULL, 0, NULL, NULL );
-        if (!(new = alloc_utf8_text( NULL, len_old + len_utf8 ))) return E_OUTOFMEMORY;
-        if (old) memcpy( new->value.bytes, utf8_old->value.bytes, len_old );
-        WideCharToMultiByte( CP_UTF8, 0, str, len, (char *)new->value.bytes + len_old, len_utf8, NULL, NULL );
+        if (!(new = alloc_utf16_text( NULL, len_old + len ))) return E_OUTOFMEMORY;
+        if (utf16_old) memcpy( new->bytes, utf16_old->bytes, len_old );
+        memcpy( new->bytes + len_old, utf16->bytes, len );
         if (offset) *offset = len_old;
         *ret = &new->text;
         return S_OK;
@@ -2477,6 +2513,15 @@ static enum record_type get_text_record_type( const WS_XML_TEXT *text, BOOL use_
         if (text_utf8->value.length <= MAX_UINT16) return RECORD_CHARS16_TEXT_WITH_ENDELEMENT;
         return RECORD_CHARS32_TEXT_WITH_ENDELEMENT;
     }
+    case WS_XML_TEXT_TYPE_UTF16:
+    {
+        const WS_XML_UTF16_TEXT *text_utf16 = (const WS_XML_UTF16_TEXT *)text;
+        int len = text_utf16->byteCount / sizeof(WCHAR);
+        int len_utf8 = WideCharToMultiByte( CP_UTF8, 0, (const WCHAR *)text_utf16->bytes, len, NULL, 0, NULL, NULL );
+        if (len_utf8 <= MAX_UINT8) return RECORD_CHARS8_TEXT_WITH_ENDELEMENT;
+        if (len_utf8 <= MAX_UINT16) return RECORD_CHARS16_TEXT_WITH_ENDELEMENT;
+        return RECORD_CHARS32_TEXT_WITH_ENDELEMENT;
+    }
     case WS_XML_TEXT_TYPE_BASE64:
     {
         const WS_XML_BASE64_TEXT *text_base64 = (const WS_XML_BASE64_TEXT *)text;
@@ -2570,24 +2615,50 @@ static HRESULT write_text_bin( struct writer *writer, const WS_XML_TEXT *text, U
     {
     case RECORD_CHARS8_TEXT_WITH_ENDELEMENT:
     {
-        const WS_XML_UTF8_TEXT *text_utf8 = (const WS_XML_UTF8_TEXT *)text;
-        UINT8 len = text_utf8->value.length;
+        const WS_XML_UTF8_TEXT *text_utf8;
+        WS_XML_UTF8_TEXT *new = NULL;
+        UINT8 len;
 
-        if ((hr = write_grow_buffer( writer, 1 + sizeof(len) + len )) != S_OK) return hr;
+        if (text->textType == WS_XML_TEXT_TYPE_UTF8) text_utf8 = (const WS_XML_UTF8_TEXT *)text;
+        else
+        {
+            if ((hr = text_to_utf8text( text, NULL, NULL, &new )) != S_OK) return hr;
+            text_utf8 = new;
+        }
+        len = text_utf8->value.length;
+        if ((hr = write_grow_buffer( writer, 1 + sizeof(len) + len )) != S_OK)
+        {
+            heap_free( new );
+            return hr;
+        }
         write_char( writer, type );
         write_char( writer, len );
         write_bytes( writer, text_utf8->value.bytes, len );
+        heap_free( new );
         return S_OK;
     }
     case RECORD_CHARS16_TEXT_WITH_ENDELEMENT:
     {
-        const WS_XML_UTF8_TEXT *text_utf8 = (const WS_XML_UTF8_TEXT *)text;
-        UINT16 len = text_utf8->value.length;
+        const WS_XML_UTF8_TEXT *text_utf8;
+        WS_XML_UTF8_TEXT *new = NULL;
+        UINT16 len;
 
-        if ((hr = write_grow_buffer( writer, 1 + sizeof(len) + len )) != S_OK) return hr;
+        if (text->textType == WS_XML_TEXT_TYPE_UTF8) text_utf8 = (const WS_XML_UTF8_TEXT *)text;
+        else
+        {
+            if ((hr = text_to_utf8text( text, NULL, NULL, &new )) != S_OK) return hr;
+            text_utf8 = new;
+        }
+        len = text_utf8->value.length;
+        if ((hr = write_grow_buffer( writer, 1 + sizeof(len) + len )) != S_OK)
+        {
+            heap_free( new );
+            return hr;
+        }
         write_char( writer, type );
         write_bytes( writer, (const BYTE *)&len, sizeof(len) );
         write_bytes( writer, text_utf8->value.bytes, len );
+        heap_free( new );
         return S_OK;
     }
     case RECORD_BYTES8_TEXT:
-- 
2.11.0




More information about the wine-devel mailing list