Use internal wcstof instead of system wcstod in formcalc lexer

This CL switches the usage of wcstod to use the FXSYS_wcstof to
determine if a given string is a valid floating point number.

Using the internal method makes linux slightly slower (10's of ms)
makes mac a lot faster 900ms to 60ms for the test case in the bug.

The FXSYS_wcstof method has been extended to handle the parsing of
float exponents. Unittests were added for FXSYS_wcstof.

Bug: chromium:813646
Change-Id: Ie68287a336e3b95a0c0b845d5bf39db6fc82b39c
Reviewed-on: https://pdfium-review.googlesource.com/32510
Reviewed-by: Ryan Harrison <rharrison@chromium.org>
Commit-Queue: dsinclair <dsinclair@chromium.org>
diff --git a/core/fxcrt/fx_extension.cpp b/core/fxcrt/fx_extension.cpp
index 83aee86..b1f2a95 100644
--- a/core/fxcrt/fx_extension.cpp
+++ b/core/fxcrt/fx_extension.cpp
@@ -50,6 +50,38 @@
       fPrecise *= 0.1f;
     }
   }
+
+  if (iUsedLen < iLength &&
+      (pwsStr[iUsedLen] == 'e' || pwsStr[iUsedLen] == 'E')) {
+    ++iUsedLen;
+
+    bool negative_exponent = false;
+    if (iUsedLen < iLength &&
+        (pwsStr[iUsedLen] == '-' || pwsStr[iUsedLen] == '+')) {
+      negative_exponent = pwsStr[iUsedLen] == '-';
+      ++iUsedLen;
+    }
+
+    size_t exp_value = 0;
+    while (iUsedLen < iLength) {
+      wchar_t wch = pwsStr[iUsedLen];
+      if (!std::iswdigit(wch))
+        break;
+
+      exp_value = exp_value * 10.0f + (wch - L'0');
+      ++iUsedLen;
+    }
+
+    for (size_t i = exp_value; i > 0; --i) {
+      if (exp_value > 0) {
+        if (negative_exponent)
+          fValue /= 10;
+        else
+          fValue *= 10;
+      }
+    }
+  }
+
   if (pUsedLen)
     *pUsedLen = iUsedLen;
 
diff --git a/core/fxcrt/fx_extension_unittest.cpp b/core/fxcrt/fx_extension_unittest.cpp
index 0500a14..f7e07c7 100644
--- a/core/fxcrt/fx_extension_unittest.cpp
+++ b/core/fxcrt/fx_extension_unittest.cpp
@@ -112,3 +112,40 @@
     }
   }
 }
+
+TEST(fxcrt, FXSYS_wcstof) {
+  int32_t used_len = 0;
+  EXPECT_FLOAT_EQ(-12.0f, FXSYS_wcstof(L"-12", 3, &used_len));
+  EXPECT_EQ(3, used_len);
+
+  used_len = 0;
+  EXPECT_FLOAT_EQ(1.5362f, FXSYS_wcstof(L"1.5362", 6, &used_len));
+  EXPECT_EQ(6, used_len);
+
+  used_len = 0;
+  EXPECT_FLOAT_EQ(0.875f, FXSYS_wcstof(L"0.875", 5, &used_len));
+  EXPECT_EQ(5, used_len);
+
+  used_len = 0;
+  EXPECT_FLOAT_EQ(5.56e-2f, FXSYS_wcstof(L"5.56e-2", 7, &used_len));
+  EXPECT_EQ(7, used_len);
+
+  used_len = 0;
+  EXPECT_FLOAT_EQ(1.234e10f, FXSYS_wcstof(L"1.234E10", 8, &used_len));
+  EXPECT_EQ(8, used_len);
+
+  // TODO(dsinclair): This should round as per IEEE 64-bit values.
+  // EXPECT_EQ(L"123456789.01234567", FXSYS_wcstof(L"123456789.012345678"));
+  used_len = 0;
+  EXPECT_FLOAT_EQ(123456789.012345678f,
+                  FXSYS_wcstof(L"123456789.012345678", 19, &used_len));
+  EXPECT_EQ(19, used_len);
+
+  // TODO(dsinclair): This is spec'd as rounding when > 16 significant digits
+  // prior to the exponent.
+  // EXPECT_EQ(100000000000000000, FXSYS_wcstof(L"99999999999999999"));
+  used_len = 0;
+  EXPECT_FLOAT_EQ(99999999999999999.0f,
+                  FXSYS_wcstof(L"99999999999999999", 17, &used_len));
+  EXPECT_EQ(17, used_len);
+}
diff --git a/xfa/fxfa/fm2js/cxfa_fmlexer.cpp b/xfa/fxfa/fm2js/cxfa_fmlexer.cpp
index 32e2957..3a69467 100644
--- a/xfa/fxfa/fm2js/cxfa_fmlexer.cpp
+++ b/xfa/fxfa/fm2js/cxfa_fmlexer.cpp
@@ -303,10 +303,12 @@
 
 CXFA_FMToken CXFA_FMLexer::AdvanceForNumber() {
   // This will set end to the character after the end of the number.
-  wchar_t* end = nullptr;
+  int32_t used_length = 0;
   if (m_cursor)
-    wcstod(const_cast<wchar_t*>(m_cursor), &end);
-  if (!end || FXSYS_iswalpha(*end)) {
+    FXSYS_wcstof(const_cast<wchar_t*>(m_cursor), -1, &used_length);
+
+  const wchar_t* end = m_cursor + used_length;
+  if (used_length == 0 || !end || FXSYS_iswalpha(*end)) {
     RaiseError();
     return CXFA_FMToken();
   }