Commit 524fa657 authored by Hans Leidekker's avatar Hans Leidekker Committed by Alexandre Julliard

webservices: Add support for reading double values.

parent 6e0366fd
......@@ -1937,6 +1937,160 @@ static HRESULT str_to_uint64( const unsigned char *str, ULONG len, UINT64 max, U
return S_OK;
}
#if defined(__i386__) || defined(__x86_64__)
#define RC_DOWN 0x100;
static BOOL set_fp_rounding( unsigned short *save )
{
#ifdef __GNUC__
unsigned short fpword;
__asm__ __volatile__( "fstcw %0" : "=m" (fpword) );
*save = fpword;
fpword |= RC_DOWN;
__asm__ __volatile__( "fldcw %0" : : "m" (fpword) );
return TRUE;
#else
FIXME( "not implemented\n" );
return FALSE;
#endif
}
static void restore_fp_rounding( unsigned short fpword )
{
#ifdef __GNUC__
__asm__ __volatile__( "fldcw %0" : : "m" (fpword) );
#else
FIXME( "not implemented\n" );
#endif
}
#else
static BOOL set_fp_rounding( unsigned short *save )
{
FIXME( "not implemented\n" );
return FALSE;
}
static void restore_fp_rounding( unsigned short fpword )
{
FIXME( "not implemented\n" );
}
#endif
static HRESULT str_to_double( const unsigned char *str, ULONG len, double *ret )
{
static const unsigned __int64 nan = 0xfff8000000000000;
static const unsigned __int64 inf = 0x7ff0000000000000;
static const unsigned __int64 inf_min = 0xfff0000000000000;
HRESULT hr = WS_E_INVALID_FORMAT;
const unsigned char *p = str, *q;
int sign = 1, exp_sign = 1, exp = 0, exp_tmp = 0, neg_exp, i, nb_digits, have_digits;
unsigned __int64 val = 0, tmp;
long double exp_val = 1.0, exp_mul = 10.0;
unsigned short fpword;
while (len && read_isspace( *p )) { p++; len--; }
while (len && read_isspace( p[len - 1] )) { len--; }
if (!len) return WS_E_INVALID_FORMAT;
if (len == 3 && !memcmp( p, "NaN", 3 ))
{
*(unsigned __int64 *)ret = nan;
return S_OK;
}
else if (len == 3 && !memcmp( p, "INF", 3 ))
{
*(unsigned __int64 *)ret = inf;
return S_OK;
}
else if (len == 4 && !memcmp( p, "-INF", 4 ))
{
*(unsigned __int64 *)ret = inf_min;
return S_OK;
}
*ret = 0.0;
if (*p == '-')
{
sign = -1;
p++; len--;
}
else if (*p == '+') { p++; len--; };
if (!len) return S_OK;
if (!set_fp_rounding( &fpword )) return E_NOTIMPL;
q = p;
while (len && isdigit( *q )) { q++; len--; }
have_digits = nb_digits = q - p;
for (i = 0; i < nb_digits; i++)
{
tmp = val * 10 + p[i] - '0';
if (val > MAX_UINT64 / 10 || tmp < val)
{
for (; i < nb_digits; i++) exp++;
break;
}
val = tmp;
}
if (len)
{
if (*q == '.')
{
p = ++q; len--;
while (len && isdigit( *q )) { q++; len--; };
have_digits |= nb_digits = q - p;
for (i = 0; i < nb_digits; i++)
{
tmp = val * 10 + p[i] - '0';
if (val > MAX_UINT64 / 10 || tmp < val) break;
val = tmp;
exp--;
}
}
if (len > 1 && tolower(*q) == 'e')
{
if (!have_digits) goto done;
p = ++q; len--;
if (*p == '-')
{
exp_sign = -1;
p++; len--;
}
else if (*p == '+') { p++; len--; };
q = p;
while (len && isdigit( *q )) { q++; len--; };
nb_digits = q - p;
if (!nb_digits || len) goto done;
for (i = 0; i < nb_digits; i++)
{
if (exp_tmp > MAX_INT32 / 10 || (exp_tmp = exp_tmp * 10 + p[i] - '0') < 0)
exp_tmp = MAX_INT32;
}
exp_tmp *= exp_sign;
if (exp < 0 && exp_tmp < 0 && exp + exp_tmp >= 0) exp = MIN_INT32;
else if (exp > 0 && exp_tmp > 0 && exp + exp_tmp < 0) exp = MAX_INT32;
else exp += exp_tmp;
}
}
if (!have_digits || len) goto done;
if ((neg_exp = exp < 0)) exp = -exp;
for (; exp; exp >>= 1)
{
if (exp & 1) exp_val *= exp_mul;
exp_mul *= exp_mul;
}
*ret = sign * (neg_exp ? val / exp_val : val * exp_val);
hr = S_OK;
done:
restore_fp_rounding( fpword );
return hr;
}
#define TICKS_PER_SEC 10000000
#define TICKS_PER_MIN (60 * (ULONGLONG)TICKS_PER_SEC)
#define TICKS_PER_HOUR (3600 * (ULONGLONG)TICKS_PER_SEC)
......@@ -2711,6 +2865,53 @@ static HRESULT read_type_uint64( struct reader *reader, WS_TYPE_MAPPING mapping,
return S_OK;
}
static HRESULT read_type_double( struct reader *reader, WS_TYPE_MAPPING mapping,
const WS_XML_STRING *localname, const WS_XML_STRING *ns,
const WS_DOUBLE_DESCRIPTION *desc, WS_READ_OPTION option,
WS_HEAP *heap, void *ret, ULONG size )
{
WS_XML_UTF8_TEXT *utf8;
HRESULT hr;
double val = 0.0;
BOOL found;
if (desc) FIXME( "ignoring description\n" );
if ((hr = read_get_text( reader, mapping, localname, ns, &utf8, &found )) != S_OK) return hr;
if (found && (hr = str_to_double( utf8->value.bytes, utf8->value.length, &val )) != S_OK) return hr;
switch (option)
{
case WS_READ_REQUIRED_VALUE:
if (!found) return WS_E_INVALID_FORMAT;
if (size != sizeof(double)) return E_INVALIDARG;
*(double *)ret = val;
break;
case WS_READ_REQUIRED_POINTER:
if (!found) return WS_E_INVALID_FORMAT;
/* fall through */
case WS_READ_OPTIONAL_POINTER:
{
double *heap_val = NULL;
if (size != sizeof(heap_val)) return E_INVALIDARG;
if (found)
{
if (!(heap_val = ws_alloc( heap, sizeof(*heap_val) ))) return WS_E_QUOTA_EXCEEDED;
*heap_val = val;
}
*(double **)ret = heap_val;
break;
}
default:
FIXME( "read option %u not supported\n", option );
return E_NOTIMPL;
}
return S_OK;
}
static HRESULT read_type_wsz( struct reader *reader, WS_TYPE_MAPPING mapping,
const WS_XML_STRING *localname, const WS_XML_STRING *ns,
const WS_WSZ_DESCRIPTION *desc, WS_READ_OPTION option,
......@@ -2958,6 +3159,9 @@ static ULONG get_type_size( WS_TYPE type, const WS_STRUCT_DESCRIPTION *desc )
case WS_UINT64_TYPE:
return sizeof(INT64);
case WS_DOUBLE_TYPE:
return sizeof(double);
case WS_DATETIME_TYPE:
return sizeof(WS_DATETIME);
......@@ -3091,6 +3295,7 @@ static WS_READ_OPTION get_field_read_option( WS_TYPE type )
case WS_UINT16_TYPE:
case WS_UINT32_TYPE:
case WS_UINT64_TYPE:
case WS_DOUBLE_TYPE:
case WS_ENUM_TYPE:
case WS_DATETIME_TYPE:
return WS_READ_REQUIRED_VALUE;
......@@ -3322,6 +3527,11 @@ static HRESULT read_type( struct reader *reader, WS_TYPE_MAPPING mapping, WS_TYP
return hr;
break;
case WS_DOUBLE_TYPE:
if ((hr = read_type_double( reader, mapping, localname, ns, desc, option, heap, value, size )) != S_OK)
return hr;
break;
case WS_WSZ_TYPE:
if ((hr = read_type_wsz( reader, mapping, localname, ns, desc, option, heap, value, size )) != S_OK)
return hr;
......
......@@ -3176,6 +3176,101 @@ static void test_WsFileTimeToDateTime(void)
ok( hr == WS_E_NUMERIC_OVERFLOW, "got %08x\n", hr );
}
static void test_double(void)
{
static const struct
{
const char *str;
HRESULT hr;
ULONGLONG val;
}
tests[] =
{
{"<t>0.0</t>", S_OK, 0},
{"<t>-0.0</t>", S_OK, 0x8000000000000000},
{"<t>+0.0</t>", S_OK, 0},
{"<t>-</t>", S_OK, 0},
{"<t>+</t>", S_OK, 0},
{"<t>.0</t>", S_OK, 0},
{"<t>0.</t>", S_OK, 0},
{"<t>0</t>", S_OK, 0},
{"<t> 0 </t>", S_OK, 0},
{"<t></t>", WS_E_INVALID_FORMAT, 0},
{"<t>0,1</t>", WS_E_INVALID_FORMAT, 0},
{"<t>1.1.</t>", WS_E_INVALID_FORMAT, 0},
{"<t>1</t>", S_OK, 0x3ff0000000000000},
{"<t>1.0000000000000002</t>", S_OK, 0x3ff0000000000001},
{"<t>1.0000000000000004</t>", S_OK, 0x3ff0000000000002},
{"<t>10000000000000000000</t>", S_OK, 0x43e158e460913d00},
{"<t>100000000000000000000</t>", S_OK, 0x4415af1d78b58c40},
{"<t>2</t>", S_OK, 0x4000000000000000},
{"<t>-2</t>", S_OK, 0xc000000000000000},
{"<t>nodouble</t>", WS_E_INVALID_FORMAT, 0},
{"<t>INF</t>", S_OK, 0x7ff0000000000000},
{"<t>-INF</t>", S_OK, 0xfff0000000000000},
{"<t>+INF</t>", WS_E_INVALID_FORMAT, 0},
{"<t>Infinity</t>", WS_E_INVALID_FORMAT, 0},
{"<t>-Infinity</t>", WS_E_INVALID_FORMAT, 0},
{"<t>inf</t>", WS_E_INVALID_FORMAT, 0},
{"<t>NaN</t>", S_OK, 0xfff8000000000000},
{"<t>-NaN</t>", WS_E_INVALID_FORMAT, 0},
{"<t>NAN</t>", WS_E_INVALID_FORMAT, 0},
{"<t>0.3</t>", S_OK, 0x3fd3333333333333},
{"<t>0.33</t>", S_OK, 0x3fd51eb851eb851f},
{"<t>0.333</t>", S_OK, 0x3fd54fdf3b645a1d},
{"<t>0.3333</t>", S_OK, 0x3fd554c985f06f69},
{"<t>0.33333</t>", S_OK, 0x3fd555475a31a4be},
{"<t>0.333333</t>", S_OK, 0x3fd55553ef6b5d46},
{"<t>0.3333333</t>", S_OK, 0x3fd55555318abc87},
{"<t>0.33333333</t>", S_OK, 0x3fd5555551c112da},
{"<t>0.333333333</t>", S_OK, 0x3fd5555554f9b516},
{"<t>0.3333333333</t>", S_OK, 0x3fd55555554c2bb5},
{"<t>0.33333333333</t>", S_OK, 0x3fd5555555546ac5},
{"<t>0.3333333333333</t>", S_OK, 0x3fd55555555552fd},
{"<t>0.33333333333333</t>", S_OK, 0x3fd5555555555519},
{"<t>0.333333333333333</t>", S_OK, 0x3fd555555555554f},
{"<t>0.3333333333333333</t>", S_OK, 0x3fd5555555555555},
{"<t>0.33333333333333333</t>", S_OK, 0x3fd5555555555555},
{"<t>0.1e10</t>", S_OK, 0x41cdcd6500000000},
{"<t>1e</t>", WS_E_INVALID_FORMAT, 0},
{"<t>1e0</t>", S_OK, 0x3ff0000000000000},
{"<t>1e+1</t>", S_OK, 0x4024000000000000},
{"<t>1e-1</t>", S_OK, 0x3fb999999999999a},
{"<t>e10</t>", WS_E_INVALID_FORMAT, 0},
{"<t>1e10.</t>", WS_E_INVALID_FORMAT, 0},
{"<t>1E10</t>", S_OK, 0x4202a05f20000000},
{"<t>1e10</t>", S_OK, 0x4202a05f20000000},
{"<t>1e-10</t>", S_OK, 0x3ddb7cdfd9d7bdbb},
{"<t>1.7976931348623158e308</t>", S_OK, 0x7fefffffffffffff},
{"<t>1.7976931348623159e308</t>", S_OK, 0x7ff0000000000000},
{"<t>4.94065645841247e-324</t>", S_OK, 0x1},
};
HRESULT hr;
WS_XML_READER *reader;
WS_HEAP *heap;
ULONGLONG val;
ULONG i;
hr = WsCreateHeap( 1 << 16, 0, NULL, 0, &heap, NULL );
ok( hr == S_OK, "got %08x\n", hr );
hr = WsCreateReader( NULL, 0, &reader, NULL ) ;
ok( hr == S_OK, "got %08x\n", hr );
for (i = 0; i < sizeof(tests)/sizeof(tests[0]); i++)
{
val = 0;
prepare_type_test( reader, tests[i].str, strlen(tests[i].str) );
hr = WsReadType( reader, WS_ELEMENT_CONTENT_TYPE_MAPPING, WS_DOUBLE_TYPE, NULL,
WS_READ_REQUIRED_VALUE, heap, &val, sizeof(val), NULL );
ok( hr == tests[i].hr, "%u: got %08x\n", i, hr );
if (hr == tests[i].hr) ok( val == tests[i].val, "%u: got %x%08x\n", i, (ULONG)(val >> 32), (ULONG)val );
}
WsFreeReader( reader );
WsFreeHeap( heap );
}
START_TEST(reader)
{
test_WsCreateError();
......@@ -3204,4 +3299,5 @@ START_TEST(reader)
test_datetime();
test_WsDateTimeToFileTime();
test_WsFileTimeToDateTime();
test_double();
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment