1
0
mirror of https://gitlab.gnome.org/GNOME/libxml2.git synced 2025-03-10 08:58:16 +03:00

Simplify XPath NaN, inf and -0 handling

Use C99 macros NAN, INFINITY, isnan, isinf. If they're not available:

- Assume that (0.0 / 0.0) generates a NaN and !(x == x) tests for NaN.
- Use C89's HUGE_VAL for INFINITY.

Remove manual handling of NaN, infinity and negative zero in functions
xmlXPathValueFlipSign and xmlXPathDivValues.

Remove xmlXPathGetSign. All the tests for negative zero can be replaced
with a test for negative or positive zero.

Simplify xmlXPathRoundFunction.

Remove Trio dependency.

This should work on IEEE 754 compliant implementations even if the C99
macros aren't available, but will likely break some ancient platforms.
If problems arise, my plan is to port the relevant trionan.c solution
to xpath.c. Note that non-compliant implementations are impossible
to fully support, anyway, since XPath requires IEEE 754.
This commit is contained in:
Nick Wellnhofer 2017-09-21 00:11:26 +02:00
parent 861823902b
commit 8813f397f8
2 changed files with 49 additions and 99 deletions

View File

@ -15,7 +15,7 @@ matrix:
- compiler: clang
dist: trusty
env: CONFIG="--without-python"
CFLAGS="-O2 -g -fno-omit-frame-pointer -fsanitize=address,undefined -fno-sanitize-recover=all"
CFLAGS="-O2 -g -fno-omit-frame-pointer -fsanitize=address,undefined -fno-sanitize=float-divide-by-zero -fno-sanitize-recover=all"
UBSAN_OPTIONS=print_stacktrace=1
script: sh autogen.sh $CONFIG && make -j2 V=1 && make check
git:

146
xpath.c
View File

@ -477,84 +477,66 @@ int wrap_cmp( xmlNodePtr x, xmlNodePtr y );
* *
************************************************************************/
#ifndef TRIO_REPLACE_STDIO
#define TRIO_PUBLIC static
#ifndef NAN
#define NAN (0.0 / 0.0)
#endif
#include "trionan.c"
/*
* The lack of portability of this section of the libc is annoying !
*/
double xmlXPathNAN = 0;
double xmlXPathPINF = 1;
double xmlXPathNINF = -1;
static double xmlXPathNZERO = 0; /* not exported from headers */
static int xmlXPathInitialized = 0;
#ifndef INFINITY
#define INFINITY HUGE_VAL
#endif
double xmlXPathNAN = NAN;
double xmlXPathPINF = INFINITY;
double xmlXPathNINF = -INFINITY;
/**
* xmlXPathInit:
*
* Initialize the XPath environment
*
* Does nothing but must be kept as public function.
*/
void
xmlXPathInit(void) {
if (xmlXPathInitialized) return;
xmlXPathPINF = trio_pinf();
xmlXPathNINF = trio_ninf();
xmlXPathNAN = trio_nan();
xmlXPathNZERO = trio_nzero();
xmlXPathInitialized = 1;
}
/**
* xmlXPathIsNaN:
* @val: a double value
*
* Provides a portable isnan() function to detect whether a double
* is a NotaNumber. Based on trio code
* http://sourceforge.net/projects/ctrio/
*
* Returns 1 if the value is a NaN, 0 otherwise
*/
int
xmlXPathIsNaN(double val) {
return(trio_isnan(val));
#ifdef isnan
return isnan(val);
#else
return !(val == val);
#endif
}
/**
* xmlXPathIsInf:
* @val: a double value
*
* Provides a portable isinf() function to detect whether a double
* is a +Infinite or -Infinite. Based on trio code
* http://sourceforge.net/projects/ctrio/
*
* Returns 1 vi the value is +Infinite, -1 if -Infinite, 0 otherwise
* Returns 1 if the value is +Infinite, -1 if -Infinite, 0 otherwise
*/
int
xmlXPathIsInf(double val) {
return(trio_isinf(val));
#ifdef isinf
return isinf(val);
#else
if (val >= HUGE_VAL)
return 1;
if (val <= -HUGE_VAL)
return -1;
return 0;
#endif
}
#endif /* SCHEMAS or XPATH */
#ifdef LIBXML_XPATH_ENABLED
/**
* xmlXPathGetSign:
* @val: a double value
*
* Provides a portable function to detect the sign of a double
* Modified from trio code
* http://sourceforge.net/projects/ctrio/
*
* Returns 1 if the value is Negative, 0 if positive
*/
static int
xmlXPathGetSign(double val) {
return(trio_signbit(val));
}
#ifdef LIBXML_XPATH_ENABLED
/*
* TODO: when compatibility allows remove all "fake node libxslt" strings
@ -1423,7 +1405,8 @@ xmlXPathDebugDumpObject(FILE *output, xmlXPathObjectPtr cur, int depth) {
default:
if (xmlXPathIsNaN(cur->floatval)) {
fprintf(output, "Object is a number : NaN\n");
} else if (cur->floatval == 0 && xmlXPathGetSign(cur->floatval) != 0) {
} else if (cur->floatval == 0) {
/* Omit sign for negative zero. */
fprintf(output, "Object is a number : 0\n");
} else {
fprintf(output, "Object is a number : %0g\n", cur->floatval);
@ -3119,7 +3102,8 @@ xmlXPathFormatNumber(double number, char buffer[], int buffersize)
if (xmlXPathIsNaN(number)) {
if (buffersize > (int)sizeof("NaN"))
snprintf(buffer, buffersize, "NaN");
} else if (number == 0 && xmlXPathGetSign(number) != 0) {
} else if (number == 0) {
/* Omit sign for negative zero. */
snprintf(buffer, buffersize, "0");
} else if ((number > INT_MIN) && (number < INT_MAX) &&
(number == (int) number)) {
@ -5728,7 +5712,8 @@ xmlXPathCastNumberToString (double val) {
default:
if (xmlXPathIsNaN(val)) {
ret = xmlStrdup((const xmlChar *) "NaN");
} else if (val == 0 && xmlXPathGetSign(val) != 0) {
} else if (val == 0) {
/* Omit sign for negative zero. */
ret = xmlStrdup((const xmlChar *) "0");
} else {
/* could be improved */
@ -5910,10 +5895,10 @@ xmlXPathCastNodeToNumber (xmlNodePtr node) {
double ret;
if (node == NULL)
return(xmlXPathNAN);
return(NAN);
strval = xmlXPathCastNodeToString(node);
if (strval == NULL)
return(xmlXPathNAN);
return(NAN);
ret = xmlXPathCastStringToNumber(strval);
xmlFree(strval);
@ -5934,7 +5919,7 @@ xmlXPathCastNodeSetToNumber (xmlNodeSetPtr ns) {
double ret;
if (ns == NULL)
return(xmlXPathNAN);
return(NAN);
str = xmlXPathCastNodeSetToString(ns);
ret = xmlXPathCastStringToNumber(str);
xmlFree(str);
@ -5954,13 +5939,13 @@ xmlXPathCastToNumber(xmlXPathObjectPtr val) {
double ret = 0.0;
if (val == NULL)
return(xmlXPathNAN);
return(NAN);
switch (val->type) {
case XPATH_UNDEFINED:
#ifdef DEGUB_EXPR
xmlGenericError(xmlGenericErrorContext, "NUMBER: undefined\n");
#endif
ret = xmlXPathNAN;
ret = NAN;
break;
case XPATH_NODESET:
case XPATH_XSLT_TREE:
@ -5980,7 +5965,7 @@ xmlXPathCastToNumber(xmlXPathObjectPtr val) {
case XPATH_RANGE:
case XPATH_LOCATIONSET:
TODO;
ret = xmlXPathNAN;
ret = NAN;
break;
}
return(ret);
@ -7484,20 +7469,7 @@ xmlXPathValueFlipSign(xmlXPathParserContextPtr ctxt) {
if ((ctxt == NULL) || (ctxt->context == NULL)) return;
CAST_TO_NUMBER;
CHECK_TYPE(XPATH_NUMBER);
if (xmlXPathIsNaN(ctxt->value->floatval))
ctxt->value->floatval=xmlXPathNAN;
else if (xmlXPathIsInf(ctxt->value->floatval) == 1)
ctxt->value->floatval=xmlXPathNINF;
else if (xmlXPathIsInf(ctxt->value->floatval) == -1)
ctxt->value->floatval=xmlXPathPINF;
else if (ctxt->value->floatval == 0) {
if (xmlXPathGetSign(ctxt->value->floatval) == 0)
ctxt->value->floatval = xmlXPathNZERO;
else
ctxt->value->floatval = 0;
}
else
ctxt->value->floatval = - ctxt->value->floatval;
ctxt->value->floatval = -ctxt->value->floatval;
}
/**
@ -7589,25 +7561,7 @@ xmlXPathDivValues(xmlXPathParserContextPtr ctxt) {
xmlXPathReleaseObject(ctxt->context, arg);
CAST_TO_NUMBER;
CHECK_TYPE(XPATH_NUMBER);
if (xmlXPathIsNaN(val) || xmlXPathIsNaN(ctxt->value->floatval))
ctxt->value->floatval = xmlXPathNAN;
else if (val == 0 && xmlXPathGetSign(val) != 0) {
if (ctxt->value->floatval == 0)
ctxt->value->floatval = xmlXPathNAN;
else if (ctxt->value->floatval > 0)
ctxt->value->floatval = xmlXPathNINF;
else if (ctxt->value->floatval < 0)
ctxt->value->floatval = xmlXPathPINF;
}
else if (val == 0) {
if (ctxt->value->floatval == 0)
ctxt->value->floatval = xmlXPathNAN;
else if (ctxt->value->floatval > 0)
ctxt->value->floatval = xmlXPathPINF;
else if (ctxt->value->floatval < 0)
ctxt->value->floatval = xmlXPathNINF;
} else
ctxt->value->floatval /= val;
ctxt->value->floatval /= val;
}
/**
@ -7632,7 +7586,7 @@ xmlXPathModValues(xmlXPathParserContextPtr ctxt) {
CHECK_TYPE(XPATH_NUMBER);
arg1 = ctxt->value->floatval;
if (arg2 == 0)
ctxt->value->floatval = xmlXPathNAN;
ctxt->value->floatval = NAN;
else {
ctxt->value->floatval = fmod(arg1, arg2);
}
@ -9751,13 +9705,9 @@ xmlXPathRoundFunction(xmlXPathParserContextPtr ctxt, int nargs) {
f = ctxt->value->floatval;
/* Test for zero to keep negative zero unchanged. */
if ((xmlXPathIsNaN(f)) || (f == 0.0))
return;
if ((f >= -0.5) && (f < 0.0)) {
/* Negative zero. */
ctxt->value->floatval = xmlXPathNZERO;
if ((f >= -0.5) && (f < 0.5)) {
/* Handles negative zero. */
ctxt->value->floatval *= 0.0;
}
else {
double rounded = floor(f);
@ -10104,7 +10054,7 @@ xmlXPathStringEvalNumber(const xmlChar *str) {
if (cur == NULL) return(0);
while (IS_BLANK_CH(*cur)) cur++;
if ((*cur != '.') && ((*cur < '0') || (*cur > '9')) && (*cur != '-')) {
return(xmlXPathNAN);
return(NAN);
}
if (*cur == '-') {
isneg = 1;
@ -10140,7 +10090,7 @@ xmlXPathStringEvalNumber(const xmlChar *str) {
cur++;
if (((*cur < '0') || (*cur > '9')) && (!ok)) {
return(xmlXPathNAN);
return(NAN);
}
while (*cur == '0') {
frac = frac + 1;
@ -10173,7 +10123,7 @@ xmlXPathStringEvalNumber(const xmlChar *str) {
}
}
while (IS_BLANK_CH(*cur)) cur++;
if (*cur != 0) return(xmlXPathNAN);
if (*cur != 0) return(NAN);
if (isneg) ret = -ret;
if (is_exponent_negative) exponent = -exponent;
ret *= pow(10.0, (double)exponent);