michael@0: /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "mozilla/FloatingPoint.h" michael@0: michael@0: #include "nsString.h" michael@0: #include "txCore.h" michael@0: #include "txXMLUtils.h" michael@0: #include michael@0: #include michael@0: #include michael@0: #ifdef WIN32 michael@0: #include michael@0: #endif michael@0: #include "prdtoa.h" michael@0: michael@0: /* michael@0: * Utility class for doubles michael@0: */ michael@0: michael@0: /* michael@0: * Converts the given String to a double, if the String value does not michael@0: * represent a double, NaN will be returned michael@0: */ michael@0: class txStringToDouble michael@0: { michael@0: public: michael@0: typedef char16_t input_type; michael@0: typedef char16_t value_type; michael@0: txStringToDouble(): mState(eWhitestart), mSign(ePositive) {} michael@0: michael@0: void michael@0: write(const input_type* aSource, uint32_t aSourceLength) michael@0: { michael@0: if (mState == eIllegal) { michael@0: return; michael@0: } michael@0: uint32_t i = 0; michael@0: char16_t c; michael@0: for ( ; i < aSourceLength; ++i) { michael@0: c = aSource[i]; michael@0: switch (mState) { michael@0: case eWhitestart: michael@0: if (c == '-') { michael@0: mState = eDecimal; michael@0: mSign = eNegative; michael@0: } michael@0: else if (c >= '0' && c <= '9') { michael@0: mState = eDecimal; michael@0: mBuffer.Append((char)c); michael@0: } michael@0: else if (c == '.') { michael@0: mState = eMantissa; michael@0: mBuffer.Append((char)c); michael@0: } michael@0: else if (!XMLUtils::isWhitespace(c)) { michael@0: mState = eIllegal; michael@0: return; michael@0: } michael@0: break; michael@0: case eDecimal: michael@0: if (c >= '0' && c <= '9') { michael@0: mBuffer.Append((char)c); michael@0: } michael@0: else if (c == '.') { michael@0: mState = eMantissa; michael@0: mBuffer.Append((char)c); michael@0: } michael@0: else if (XMLUtils::isWhitespace(c)) { michael@0: mState = eWhiteend; michael@0: } michael@0: else { michael@0: mState = eIllegal; michael@0: return; michael@0: } michael@0: break; michael@0: case eMantissa: michael@0: if (c >= '0' && c <= '9') { michael@0: mBuffer.Append((char)c); michael@0: } michael@0: else if (XMLUtils::isWhitespace(c)) { michael@0: mState = eWhiteend; michael@0: } michael@0: else { michael@0: mState = eIllegal; michael@0: return; michael@0: } michael@0: break; michael@0: case eWhiteend: michael@0: if (!XMLUtils::isWhitespace(c)) { michael@0: mState = eIllegal; michael@0: return; michael@0: } michael@0: break; michael@0: default: michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: michael@0: double michael@0: getDouble() michael@0: { michael@0: if (mState == eIllegal || mBuffer.IsEmpty() || michael@0: (mBuffer.Length() == 1 && mBuffer[0] == '.')) { michael@0: return mozilla::UnspecifiedNaN(); michael@0: } michael@0: return mSign*PR_strtod(mBuffer.get(), 0); michael@0: } michael@0: private: michael@0: nsAutoCString mBuffer; michael@0: enum { michael@0: eWhitestart, michael@0: eDecimal, michael@0: eMantissa, michael@0: eWhiteend, michael@0: eIllegal michael@0: } mState; michael@0: enum { michael@0: eNegative = -1, michael@0: ePositive = 1 michael@0: } mSign; michael@0: }; michael@0: michael@0: double txDouble::toDouble(const nsAString& aSrc) michael@0: { michael@0: txStringToDouble sink; michael@0: nsAString::const_iterator fromBegin, fromEnd; michael@0: copy_string(aSrc.BeginReading(fromBegin), aSrc.EndReading(fromEnd), sink); michael@0: return sink.getDouble(); michael@0: } michael@0: michael@0: /* michael@0: * Converts the value of the given double to a String, and places michael@0: * The result into the destination String. michael@0: * @return the given dest string michael@0: */ michael@0: void txDouble::toString(double aValue, nsAString& aDest) michael@0: { michael@0: michael@0: // check for special cases michael@0: michael@0: if (mozilla::IsNaN(aValue)) { michael@0: aDest.AppendLiteral("NaN"); michael@0: return; michael@0: } michael@0: if (mozilla::IsInfinite(aValue)) { michael@0: if (aValue < 0) michael@0: aDest.Append(char16_t('-')); michael@0: aDest.AppendLiteral("Infinity"); michael@0: return; michael@0: } michael@0: michael@0: // Mantissa length is 17, so this is plenty michael@0: const int buflen = 20; michael@0: char buf[buflen]; michael@0: michael@0: int intDigits, sign; michael@0: char* endp; michael@0: PR_dtoa(aValue, 0, 0, &intDigits, &sign, &endp, buf, buflen - 1); michael@0: michael@0: // compute length michael@0: int32_t length = endp - buf; michael@0: if (length > intDigits) { michael@0: // decimal point needed michael@0: ++length; michael@0: if (intDigits < 1) { michael@0: // leading zeros, -intDigits + 1 michael@0: length += 1 - intDigits; michael@0: } michael@0: } michael@0: else { michael@0: // trailing zeros, total length given by intDigits michael@0: length = intDigits; michael@0: } michael@0: if (aValue < 0) michael@0: ++length; michael@0: // grow the string michael@0: uint32_t oldlength = aDest.Length(); michael@0: if (!aDest.SetLength(oldlength + length, mozilla::fallible_t())) michael@0: return; // out of memory michael@0: nsAString::iterator dest; michael@0: aDest.BeginWriting(dest).advance(int32_t(oldlength)); michael@0: if (aValue < 0) { michael@0: *dest = '-'; ++dest; michael@0: } michael@0: int i; michael@0: // leading zeros michael@0: if (intDigits < 1) { michael@0: *dest = '0'; ++dest; michael@0: *dest = '.'; ++dest; michael@0: for (i = 0; i > intDigits; --i) { michael@0: *dest = '0'; ++dest; michael@0: } michael@0: } michael@0: // mantissa michael@0: int firstlen = std::min(intDigits, endp - buf); michael@0: for (i = 0; i < firstlen; i++) { michael@0: *dest = buf[i]; ++dest; michael@0: } michael@0: if (i < endp - buf) { michael@0: if (i > 0) { michael@0: *dest = '.'; ++dest; michael@0: } michael@0: for (; i < endp - buf; i++) { michael@0: *dest = buf[i]; ++dest; michael@0: } michael@0: } michael@0: // trailing zeros michael@0: for (; i < intDigits; i++) { michael@0: *dest = '0'; ++dest; michael@0: } michael@0: }