/* * Converts the value of the given double to a String, and places * The result into the destination String. * @return the given dest string */ void txDouble::toString(double aValue, nsAString& aDest) { // check for special cases if (MOZ_DOUBLE_IS_NaN(aValue)) { aDest.AppendLiteral("NaN"); return; } if (MOZ_DOUBLE_IS_INFINITE(aValue)) { if (aValue < 0) aDest.Append(PRUnichar('-')); aDest.AppendLiteral("Infinity"); return; } // Mantissa length is 17, so this is plenty const int buflen = 20; char buf[buflen]; int intDigits, sign; char* endp; PR_dtoa(aValue, 0, 0, &intDigits, &sign, &endp, buf, buflen - 1); // compute length int32_t length = endp - buf; if (length > intDigits) { // decimal point needed ++length; if (intDigits < 1) { // leading zeros, -intDigits + 1 length += 1 - intDigits; } } else { // trailing zeros, total length given by intDigits length = intDigits; } if (aValue < 0) ++length; // grow the string uint32_t oldlength = aDest.Length(); if (!EnsureStringLength(aDest, oldlength + length)) return; // out of memory nsAString::iterator dest; aDest.BeginWriting(dest).advance(int32_t(oldlength)); if (aValue < 0) { *dest = '-'; ++dest; } int i; // leading zeros if (intDigits < 1) { *dest = '0'; ++dest; *dest = '.'; ++dest; for (i = 0; i > intDigits; --i) { *dest = '0'; ++dest; } } // mantissa int firstlen = std::min<size_t>(intDigits, endp - buf); for (i = 0; i < firstlen; i++) { *dest = buf[i]; ++dest; } if (i < endp - buf) { if (i > 0) { *dest = '.'; ++dest; } for (; i < endp - buf; i++) { *dest = buf[i]; ++dest; } } // trailing zeros for (; i < intDigits; i++) { *dest = '0'; ++dest; } }
/* ** Convert a double precision floating point number into its printable ** form. */ int nsTextFormatter::cvt_f(SprintfStateStr* aState, double aDouble, int aWidth, int aPrec, const char16_t aType, int aFlags) { int mode = 2; int decpt; int sign; char buf[256]; char* bufp = buf; int bufsz = 256; char num[256]; char* nump; char* endnum; int numdigits = 0; char exp = 'e'; if (aPrec == -1) { aPrec = 6; } else if (aPrec > 50) { // limit precision to avoid PR_dtoa bug 108335 // and to prevent buffers overflows aPrec = 50; } switch (aType) { case 'f': numdigits = aPrec; mode = 3; break; case 'E': exp = 'E'; MOZ_FALLTHROUGH; case 'e': numdigits = aPrec + 1; mode = 2; break; case 'G': exp = 'E'; MOZ_FALLTHROUGH; case 'g': if (aPrec == 0) { aPrec = 1; } numdigits = aPrec; mode = 2; break; default: NS_ERROR("invalid aType passed to cvt_f"); } if (PR_dtoa(aDouble, mode, numdigits, &decpt, &sign, &endnum, num, bufsz) == PR_FAILURE) { buf[0] = '\0'; return -1; } numdigits = endnum - num; nump = num; if (sign) { *bufp++ = '-'; } else if (aFlags & _SIGNED) { *bufp++ = '+'; } if (decpt == 9999) { while ((*bufp++ = *nump++)) { } } else { switch (aType) { case 'E': case 'e': *bufp++ = *nump++; if (aPrec > 0) { *bufp++ = '.'; while (*nump) { *bufp++ = *nump++; aPrec--; } while (aPrec-- > 0) { *bufp++ = '0'; } } *bufp++ = exp; ::snprintf(bufp, bufsz - (bufp - buf), "%+03d", decpt - 1); break; case 'f': if (decpt < 1) { *bufp++ = '0'; if (aPrec > 0) { *bufp++ = '.'; while (decpt++ && aPrec-- > 0) { *bufp++ = '0'; } while (*nump && aPrec-- > 0) { *bufp++ = *nump++; } while (aPrec-- > 0) { *bufp++ = '0'; } } } else { while (*nump && decpt-- > 0) { *bufp++ = *nump++; } while (decpt-- > 0) { *bufp++ = '0'; } if (aPrec > 0) { *bufp++ = '.'; while (*nump && aPrec-- > 0) { *bufp++ = *nump++; } while (aPrec-- > 0) { *bufp++ = '0'; } } } *bufp = '\0'; break; case 'G': case 'g': if ((decpt < -3) || ((decpt - 1) >= aPrec)) { *bufp++ = *nump++; numdigits--; if (numdigits > 0) { *bufp++ = '.'; while (*nump) { *bufp++ = *nump++; } } *bufp++ = exp; ::snprintf(bufp, bufsz - (bufp - buf), "%+03d", decpt - 1); } else { if (decpt < 1) { *bufp++ = '0'; if (aPrec > 0) { *bufp++ = '.'; while (decpt++) { *bufp++ = '0'; } while (*nump) { *bufp++ = *nump++; } } } else { while (*nump && decpt-- > 0) { *bufp++ = *nump++; numdigits--; } while (decpt-- > 0) { *bufp++ = '0'; } if (numdigits > 0) { *bufp++ = '.'; while (*nump) { *bufp++ = *nump++; } } } *bufp = '\0'; } } } char16_t rbuf[256]; char16_t* rbufp = rbuf; bufp = buf; // cast to char16_t while ((*rbufp++ = *bufp++)) { } *rbufp = '\0'; return fill2(aState, rbuf, NS_strlen(rbuf), aWidth, aFlags); }
/** * This is a copy of |PR_cnvtf| with a bug fixed. (The second argument * of PR_dtoa is 2 rather than 1.) * * XXX(darin): if this is the right thing, then why wasn't it fixed in NSPR?!? */ static void Modified_cnvtf(char *buf, int bufsz, int prcsn, double fval) { PRIntn decpt, sign, numdigits; char *num, *nump; char *bufp = buf; char *endnum; /* If anything fails, we store an empty string in 'buf' */ num = (char*)malloc(bufsz); if (num == NULL) { buf[0] = '\0'; return; } if (PR_dtoa(fval, 2, prcsn, &decpt, &sign, &endnum, num, bufsz) == PR_FAILURE) { buf[0] = '\0'; goto done; } numdigits = endnum - num; nump = num; /* * The NSPR code had a fancy way of checking that we weren't dealing * with -0.0 or -NaN, but I'll just use < instead. * XXX Should we check !isnan(fval) as well? Is it portable? We * probably don't need to bother since NAN isn't portable. */ if (sign && fval < 0.0f) { *bufp++ = '-'; } if (decpt == 9999) { while ((*bufp++ = *nump++) != 0) {} /* nothing to execute */ goto done; } if (decpt > (prcsn+1) || decpt < -(prcsn-1) || decpt < -5) { *bufp++ = *nump++; if (numdigits != 1) { *bufp++ = '.'; } while (*nump != '\0') { *bufp++ = *nump++; } *bufp++ = 'e'; PR_snprintf(bufp, bufsz - (bufp - buf), "%+d", decpt-1); } else if (decpt >= 0) { if (decpt == 0) { *bufp++ = '0'; } else { while (decpt--) { if (*nump != '\0') { *bufp++ = *nump++; } else { *bufp++ = '0'; } } } if (*nump != '\0') { *bufp++ = '.'; while (*nump != '\0') { *bufp++ = *nump++; } } *bufp++ = '\0'; } else if (decpt < 0) { *bufp++ = '0'; *bufp++ = '.'; while (decpt++) { *bufp++ = '0'; } while (*nump != '\0') { *bufp++ = *nump++; } *bufp++ = '\0'; } done: free(num); }
/** * This is a copy of |PR_cnvtf| with a bug fixed. (The second argument * of PR_dtoa is 2 rather than 1.) * * XXX(darin): if this is the right thing, then why wasn't it fixed in NSPR?!? */ static void Modified_cnvtf(char (& buf)[40], int prcsn, double fval) { int decpt, sign, numdigits; char num[40]; char *nump; char *bufp = buf; char *endnum; if (PR_dtoa(fval, 2, prcsn, &decpt, &sign, &endnum, num, sizeof(num)) == PR_FAILURE) { buf[0] = '\0'; return; } numdigits = endnum - num; nump = num; /* * The NSPR code had a fancy way of checking that we weren't dealing * with -0.0 or -NaN, but I'll just use < instead. * XXX Should we check !isnan(fval) as well? Is it portable? We * probably don't need to bother since NAN isn't portable. */ if (sign && fval < 0.0f) { *bufp++ = '-'; } if (decpt == 9999) { while ((*bufp++ = *nump++) != 0) {} /* nothing to execute */ return; } if (decpt > (prcsn+1) || decpt < -(prcsn-1) || decpt < -5) { *bufp++ = *nump++; if (numdigits != 1) { *bufp++ = '.'; } while (*nump != '\0') { *bufp++ = *nump++; } *bufp++ = 'e'; PR_snprintf(bufp, sizeof(num) - (bufp - buf), "%+d", decpt-1); } else if (decpt >= 0) { if (decpt == 0) { *bufp++ = '0'; } else { while (decpt--) { if (*nump != '\0') { *bufp++ = *nump++; } else { *bufp++ = '0'; } } } if (*nump != '\0') { *bufp++ = '.'; while (*nump != '\0') { *bufp++ = *nump++; } } *bufp++ = '\0'; } else if (decpt < 0) { *bufp++ = '0'; *bufp++ = '.'; while (decpt++) { *bufp++ = '0'; } while (*nump != '\0') { *bufp++ = *nump++; } *bufp++ = '\0'; } }
/* * Evaluates this Expr based on the given context node and processor state * @param context the context node for evaluation of this Expr * @param cs the ContextState containing the stack information needed * for evaluation * @return the result of the evaluation */ nsresult txFormatNumberFunctionCall::evaluate(txIEvalContext* aContext, txAExprResult** aResult) { *aResult = nullptr; if (!requireParams(2, 3, aContext)) return NS_ERROR_XPATH_BAD_ARGUMENT_COUNT; // Get number and format double value; txExpandedName formatName; nsresult rv = evaluateToNumber(mParams[0], aContext, &value); NS_ENSURE_SUCCESS(rv, rv); nsAutoString formatStr; rv = mParams[1]->evaluateToString(aContext, formatStr); NS_ENSURE_SUCCESS(rv, rv); if (mParams.Length() == 3) { nsAutoString formatQName; rv = mParams[2]->evaluateToString(aContext, formatQName); NS_ENSURE_SUCCESS(rv, rv); rv = formatName.init(formatQName, mMappings, false); NS_ENSURE_SUCCESS(rv, rv); } txDecimalFormat* format = mStylesheet->getDecimalFormat(formatName); if (!format) { nsAutoString err(NS_LITERAL_STRING("unknown decimal format")); #ifdef TX_TO_STRING err.AppendLiteral(" for: "); toString(err); #endif aContext->receiveError(err, NS_ERROR_XPATH_INVALID_ARG); return NS_ERROR_XPATH_INVALID_ARG; } // Special cases if (mozilla::IsNaN(value)) { return aContext->recycler()->getStringResult(format->mNaN, aResult); } if (value == mozilla::PositiveInfinity<double>()) { return aContext->recycler()->getStringResult(format->mInfinity, aResult); } if (value == mozilla::NegativeInfinity<double>()) { nsAutoString res; res.Append(format->mMinusSign); res.Append(format->mInfinity); return aContext->recycler()->getStringResult(res, aResult); } // Value is a normal finite number nsAutoString prefix; nsAutoString suffix; int minIntegerSize=0; int minFractionSize=0; int maxFractionSize=0; int multiplier=1; int groupSize=-1; uint32_t pos = 0; uint32_t formatLen = formatStr.Length(); bool inQuote; // Get right subexpression inQuote = false; if (mozilla::IsNegative(value)) { while (pos < formatLen && (inQuote || formatStr.CharAt(pos) != format->mPatternSeparator)) { if (formatStr.CharAt(pos) == FORMAT_QUOTE) inQuote = !inQuote; pos++; } if (pos == formatLen) { pos = 0; prefix.Append(format->mMinusSign); } else pos++; } // Parse the format string FormatParseState pState = Prefix; inQuote = false; char16_t c = 0; while (pos < formatLen && pState != Finished) { c=formatStr.CharAt(pos++); switch (pState) { case Prefix: case Suffix: if (!inQuote) { if (c == format->mPercent) { if (multiplier == 1) multiplier = 100; else { nsAutoString err(INVALID_PARAM_VALUE); #ifdef TX_TO_STRING err.AppendLiteral(": "); toString(err); #endif aContext->receiveError(err, NS_ERROR_XPATH_INVALID_ARG); return NS_ERROR_XPATH_INVALID_ARG; } } else if (c == format->mPerMille) { if (multiplier == 1) multiplier = 1000; else { nsAutoString err(INVALID_PARAM_VALUE); #ifdef TX_TO_STRING err.AppendLiteral(": "); toString(err); #endif aContext->receiveError(err, NS_ERROR_XPATH_INVALID_ARG); return NS_ERROR_XPATH_INVALID_ARG; } } else if (c == format->mDecimalSeparator || c == format->mGroupingSeparator || c == format->mZeroDigit || c == format->mDigit || c == format->mPatternSeparator) { pState = pState == Prefix ? IntDigit : Finished; pos--; break; } } if (c == FORMAT_QUOTE) inQuote = !inQuote; else if (pState == Prefix) prefix.Append(c); else suffix.Append(c); break; case IntDigit: if (c == format->mGroupingSeparator) groupSize=0; else if (c == format->mDigit) { if (groupSize >= 0) groupSize++; } else { pState = IntZero; pos--; } break; case IntZero: if (c == format->mGroupingSeparator) groupSize = 0; else if (c == format->mZeroDigit) { if (groupSize >= 0) groupSize++; minIntegerSize++; } else if (c == format->mDecimalSeparator) { pState = FracZero; } else { pState = Suffix; pos--; } break; case FracZero: if (c == format->mZeroDigit) { maxFractionSize++; minFractionSize++; } else { pState = FracDigit; pos--; } break; case FracDigit: if (c == format->mDigit) maxFractionSize++; else { pState = Suffix; pos--; } break; case Finished: break; } } // Did we manage to parse the entire formatstring and was it valid if ((c != format->mPatternSeparator && pos < formatLen) || inQuote || groupSize == 0) { nsAutoString err(INVALID_PARAM_VALUE); #ifdef TX_TO_STRING err.AppendLiteral(": "); toString(err); #endif aContext->receiveError(err, NS_ERROR_XPATH_INVALID_ARG); return NS_ERROR_XPATH_INVALID_ARG; } /* * FINALLY we're done with the parsing * now build the result string */ value = fabs(value) * multiplier; // Prefix nsAutoString res(prefix); int bufsize; if (value > 1) bufsize = (int)log10(value) + 30; else bufsize = 1 + 30; char* buf = new char[bufsize]; NS_ENSURE_TRUE(buf, NS_ERROR_OUT_OF_MEMORY); int bufIntDigits, sign; char* endp; PR_dtoa(value, 0, 0, &bufIntDigits, &sign, &endp, buf, bufsize-1); int buflen = endp - buf; int intDigits; intDigits = bufIntDigits > minIntegerSize ? bufIntDigits : minIntegerSize; if (groupSize < 0) groupSize = intDigits + 10; //to simplify grouping // XXX We shouldn't use SetLength. res.SetLength(res.Length() + intDigits + // integer digits 1 + // decimal separator maxFractionSize + // fractions (intDigits-1)/groupSize); // group separators int32_t i = bufIntDigits + maxFractionSize - 1; bool carry = (0 <= i+1) && (i+1 < buflen) && (buf[i+1] >= '5'); bool hasFraction = false; uint32_t resPos = res.Length()-1; // Fractions for (; i >= bufIntDigits; --i) { int digit; if (i >= buflen || i < 0) { digit = 0; } else { digit = buf[i] - '0'; } if (carry) { digit = (digit + 1) % 10; carry = digit == 0; } if (hasFraction || digit != 0 || i < bufIntDigits+minFractionSize) { hasFraction = true; res.SetCharAt((char16_t)(digit + format->mZeroDigit), resPos--); } else { res.Truncate(resPos--); } } // Decimal separator if (hasFraction) { res.SetCharAt(format->mDecimalSeparator, resPos--); } else { res.Truncate(resPos--); } // Integer digits for (i = 0; i < intDigits; ++i) { int digit; if (bufIntDigits-i-1 >= buflen || bufIntDigits-i-1 < 0) { digit = 0; } else { digit = buf[bufIntDigits-i-1] - '0'; } if (carry) { digit = (digit + 1) % 10; carry = digit == 0; } if (i != 0 && i%groupSize == 0) { res.SetCharAt(format->mGroupingSeparator, resPos--); } res.SetCharAt((char16_t)(digit + format->mZeroDigit), resPos--); } if (carry) { if (i%groupSize == 0) { res.Insert(format->mGroupingSeparator, resPos + 1); } res.Insert((char16_t)(1 + format->mZeroDigit), resPos + 1); } if (!hasFraction && !intDigits && !carry) { // If we havn't added any characters we add a '0' // This can only happen for formats like '##.##' res.Append(format->mZeroDigit); } delete [] buf; // Build suffix res.Append(suffix); return aContext->recycler()->getStringResult(res, aResult); } //-- evaluate