webservices: Don't use the dictionary for UTF-16 text.

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@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Hans Leidekker 2017-12-08 15:49:41 +01:00 committed by Alexandre Julliard
parent 8b11866534
commit f85c6f283a
3 changed files with 490 additions and 406 deletions

View File

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

View File

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

View File

@ -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,6 +793,390 @@ static BOOL get_string_id( struct writer *writer, const WS_XML_STRING *str, ULON
return FALSE;
}
static ULONG format_bool( const BOOL *ptr, unsigned char *buf )
{
static const unsigned char bool_true[] = {'t','r','u','e'}, bool_false[] = {'f','a','l','s','e'};
if (*ptr)
{
memcpy( buf, bool_true, sizeof(bool_true) );
return sizeof(bool_true);
}
memcpy( buf, bool_false, sizeof(bool_false) );
return sizeof(bool_false);
}
static ULONG format_int32( const INT32 *ptr, unsigned char *buf )
{
return wsprintfA( (char *)buf, "%d", *ptr );
}
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 ))
{
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;
}
if ((neg = val < 0))
{
*p++ = '-';
val = -val;
}
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;
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--;
}
if (use_exp)
{
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];
}
}
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)
{
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;
}
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 ULONG format_urn( const GUID *ptr, unsigned char *buf )
{
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;
@ -805,25 +1198,54 @@ static HRESULT write_attribute_value_bin( struct writer *writer, const WS_XML_TE
{
case RECORD_CHARS8_TEXT:
{
WS_XML_UTF8_TEXT *text_utf8 = (WS_XML_UTF8_TEXT *)text;
if (!text_utf8)
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 ((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 );
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:
{
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;
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:
@ -1832,390 +2254,6 @@ HRESULT WINAPI WsWriteStartElement( WS_XML_WRITER *handle, const WS_XML_STRING *
return hr;
}
static ULONG format_bool( const BOOL *ptr, unsigned char *buf )
{
static const unsigned char bool_true[] = {'t','r','u','e'}, bool_false[] = {'f','a','l','s','e'};
if (*ptr)
{
memcpy( buf, bool_true, sizeof(bool_true) );
return sizeof(bool_true);
}
memcpy( buf, bool_false, sizeof(bool_false) );
return sizeof(bool_false);
}
static ULONG format_int32( const INT32 *ptr, unsigned char *buf )
{
return wsprintfA( (char *)buf, "%d", *ptr );
}
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 ))
{
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;
}
if ((neg = val < 0))
{
*p++ = '-';
val = -val;
}
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;
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--;
}
if (use_exp)
{
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];
}
}
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)
{
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;
}
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 ULONG format_urn( const GUID *ptr, unsigned char *buf )
{
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 text_to_text( const WS_XML_TEXT *text, const WS_XML_TEXT *old, ULONG *offset, WS_XML_TEXT **ret )
{
if (offset) *offset = 0;
@ -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: