1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 
6 #include "mozilla/FloatingPoint.h"
7 
8 #include "nsString.h"
9 #include "txCore.h"
10 #include "txXMLUtils.h"
11 #include <math.h>
12 #include <stdlib.h>
13 #include <algorithm>
14 #ifdef WIN32
15 #  include <float.h>
16 #endif
17 #include "prdtoa.h"
18 
19 /*
20  * Utility class for doubles
21  */
22 
23 /*
24  * Converts the given String to a double, if the String value does not
25  * represent a double, NaN will be returned
26  */
27 class txStringToDouble {
28  public:
txStringToDouble()29   txStringToDouble() : mState(eWhitestart), mSign(ePositive) {}
30 
Parse(const nsAString & aSource)31   void Parse(const nsAString& aSource) {
32     if (mState == eIllegal) {
33       return;
34     }
35     uint32_t i = 0;
36     char16_t c;
37     auto len = aSource.Length();
38     for (; i < len; ++i) {
39       c = aSource[i];
40       switch (mState) {
41         case eWhitestart:
42           if (c == '-') {
43             mState = eDecimal;
44             mSign = eNegative;
45           } else if (c >= '0' && c <= '9') {
46             mState = eDecimal;
47             mBuffer.Append((char)c);
48           } else if (c == '.') {
49             mState = eMantissa;
50             mBuffer.Append((char)c);
51           } else if (!XMLUtils::isWhitespace(c)) {
52             mState = eIllegal;
53             return;
54           }
55           break;
56         case eDecimal:
57           if (c >= '0' && c <= '9') {
58             mBuffer.Append((char)c);
59           } else if (c == '.') {
60             mState = eMantissa;
61             mBuffer.Append((char)c);
62           } else if (XMLUtils::isWhitespace(c)) {
63             mState = eWhiteend;
64           } else {
65             mState = eIllegal;
66             return;
67           }
68           break;
69         case eMantissa:
70           if (c >= '0' && c <= '9') {
71             mBuffer.Append((char)c);
72           } else if (XMLUtils::isWhitespace(c)) {
73             mState = eWhiteend;
74           } else {
75             mState = eIllegal;
76             return;
77           }
78           break;
79         case eWhiteend:
80           if (!XMLUtils::isWhitespace(c)) {
81             mState = eIllegal;
82             return;
83           }
84           break;
85         default:
86           break;
87       }
88     }
89   }
90 
getDouble()91   double getDouble() {
92     if (mState == eIllegal || mBuffer.IsEmpty() ||
93         (mBuffer.Length() == 1 && mBuffer[0] == '.')) {
94       return mozilla::UnspecifiedNaN<double>();
95     }
96     return mSign * PR_strtod(mBuffer.get(), 0);
97   }
98 
99  private:
100   nsAutoCString mBuffer;
101   enum { eWhitestart, eDecimal, eMantissa, eWhiteend, eIllegal } mState;
102   enum { eNegative = -1, ePositive = 1 } mSign;
103 };
104 
toDouble(const nsAString & aSrc)105 double txDouble::toDouble(const nsAString& aSrc) {
106   txStringToDouble sink;
107   sink.Parse(aSrc);
108   return sink.getDouble();
109 }
110 
111 /*
112  * Converts the value of the given double to a String, and places
113  * The result into the destination String.
114  * @return the given dest string
115  */
toString(double aValue,nsAString & aDest)116 void txDouble::toString(double aValue, nsAString& aDest) {
117   // check for special cases
118 
119   if (mozilla::IsNaN(aValue)) {
120     aDest.AppendLiteral("NaN");
121     return;
122   }
123   if (mozilla::IsInfinite(aValue)) {
124     if (aValue < 0) aDest.Append(char16_t('-'));
125     aDest.AppendLiteral("Infinity");
126     return;
127   }
128 
129   // Mantissa length is 17, so this is plenty
130   const int buflen = 20;
131   char buf[buflen];
132 
133   int intDigits, sign;
134   char* endp;
135   PR_dtoa(aValue, 0, 0, &intDigits, &sign, &endp, buf, buflen - 1);
136 
137   // compute length
138   int32_t length = endp - buf;
139   if (length > intDigits) {
140     // decimal point needed
141     ++length;
142     if (intDigits < 1) {
143       // leading zeros, -intDigits + 1
144       length += 1 - intDigits;
145     }
146   } else {
147     // trailing zeros, total length given by intDigits
148     length = intDigits;
149   }
150   if (aValue < 0) ++length;
151   // grow the string
152   uint32_t oldlength = aDest.Length();
153   if (!aDest.SetLength(oldlength + length, mozilla::fallible))
154     return;  // out of memory
155   auto dest = aDest.BeginWriting();
156   std::advance(dest, oldlength);
157   if (aValue < 0) {
158     *dest = '-';
159     ++dest;
160   }
161   int i;
162   // leading zeros
163   if (intDigits < 1) {
164     *dest = '0';
165     ++dest;
166     *dest = '.';
167     ++dest;
168     for (i = 0; i > intDigits; --i) {
169       *dest = '0';
170       ++dest;
171     }
172   }
173   // mantissa
174   int firstlen = std::min<size_t>(intDigits, endp - buf);
175   for (i = 0; i < firstlen; i++) {
176     *dest = buf[i];
177     ++dest;
178   }
179   if (i < endp - buf) {
180     if (i > 0) {
181       *dest = '.';
182       ++dest;
183     }
184     for (; i < endp - buf; i++) {
185       *dest = buf[i];
186       ++dest;
187     }
188   }
189   // trailing zeros
190   for (; i < intDigits; i++) {
191     *dest = '0';
192     ++dest;
193   }
194 }
195