tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

txDouble.cpp (4694B)


      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 <math.h>
      7 
      8 #include <algorithm>
      9 
     10 #include "mozilla/FloatingPoint.h"
     11 #include "nsString.h"
     12 #include "txCore.h"
     13 #include "txXMLUtils.h"
     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:
     29  txStringToDouble() : mState(eWhitestart), mSign(ePositive) {}
     30 
     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 
     91  double getDouble() {
     92    if (mState == eIllegal || mBuffer.IsEmpty() ||
     93        (mBuffer.Length() == 1 && mBuffer[0] == '.')) {
     94      return mozilla::UnspecifiedNaN<double>();
     95    }
     96    return static_cast<double>(mSign) * PR_strtod(mBuffer.get(), nullptr);
     97  }
     98 
     99 private:
    100  nsAutoCString mBuffer;
    101  enum { eWhitestart, eDecimal, eMantissa, eWhiteend, eIllegal } mState;
    102  enum { eNegative = -1, ePositive = 1 } mSign;
    103 };
    104 
    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 */
    116 void txDouble::toString(double aValue, nsAString& aDest) {
    117  // check for special cases
    118 
    119  if (std::isnan(aValue)) {
    120    aDest.AppendLiteral("NaN");
    121    return;
    122  }
    123  if (std::isinf(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 }