Commit 75b27377 authored by Robert Sesek's avatar Robert Sesek Committed by Commit Bot

Introduce //base/third_party/double_conversion for string<>double conversion.

This will replace //base/third_party/dmg_fp. dmg_fp takes at least one
mutex for every conversion, which is sub-optimal for performance. The
new double-conversion library comes from V8, and it is also used in
Blink. A future CL will move Blink onto this copy in //base.

Compatibility risk is high because things may rely on specific output
output behaviors of the old implementation.

Bug: 95729
Change-Id: I6b08093ff4e38e257629f1fcf1d4f095e170c24a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1698345
Commit-Queue: Robert Sesek <rsesek@chromium.org>
Reviewed-by: default avatarNico Weber <thakis@chromium.org>
Cr-Commit-Position: refs/heads/master@{#691618}
parent 33d1685b
......@@ -86,10 +86,6 @@ if (is_fuchsia) {
config("base_flags") {
if (is_clang) {
cflags = [
# Don't die on dtoa code that uses a char as an array index.
# This is required solely for base/third_party/dmg_fp/dtoa_wrapper.cc.
"-Wno-char-subscripts",
# Ideally all product code (but no test code) in chrome would have these
# flags. But this isn't trivial so start with //base as a minimum
# requirement.
......@@ -881,9 +877,6 @@ jumbo_component("base") {
"test/malloc_wrapper.h",
"third_party/cityhash/city.cc",
"third_party/cityhash/city.h",
"third_party/dmg_fp/dmg_fp.h",
"third_party/dmg_fp/dtoa_wrapper.cc",
"third_party/dmg_fp/g_fmt.cc",
"third_party/icu/icu_utf.cc",
"third_party/icu/icu_utf.h",
"third_party/nspr/prtime.cc",
......@@ -1249,6 +1242,7 @@ jumbo_component("base") {
deps = [
"//base/allocator",
"//base/allocator:buildflags",
"//base/third_party/double_conversion",
"//base/third_party/dynamic_annotations",
"//third_party/modp_b64",
]
......
......@@ -13,10 +13,10 @@
#include <type_traits>
#include "base/logging.h"
#include "base/no_destructor.h"
#include "base/numerics/safe_math.h"
#include "base/scoped_clear_last_error.h"
#include "base/strings/utf_string_conversions.h"
#include "base/third_party/dmg_fp/dmg_fp.h"
#include "base/third_party/double_conversion/double-conversion/double-conversion.h"
namespace base {
......@@ -360,21 +360,29 @@ string16 NumberToString16(unsigned long long value) {
return IntToStringT<string16, unsigned long long>::IntToString(value);
}
static const double_conversion::DoubleToStringConverter*
GetDoubleToStringConverter() {
static NoDestructor<double_conversion::DoubleToStringConverter> converter(
double_conversion::DoubleToStringConverter::EMIT_POSITIVE_EXPONENT_SIGN,
nullptr, nullptr, 'e', -6, 12, 0, 0);
return converter.get();
}
std::string NumberToString(double value) {
// According to g_fmt.cc, it is sufficient to declare a buffer of size 32.
char buffer[32];
dmg_fp::g_fmt(buffer, value);
return std::string(buffer);
double_conversion::StringBuilder builder(buffer, sizeof(buffer));
GetDoubleToStringConverter()->ToShortest(value, &builder);
return std::string(buffer, builder.position());
}
base::string16 NumberToString16(double value) {
// According to g_fmt.cc, it is sufficient to declare a buffer of size 32.
char buffer[32];
dmg_fp::g_fmt(buffer, value);
double_conversion::StringBuilder builder(buffer, sizeof(buffer));
GetDoubleToStringConverter()->ToShortest(value, &builder);
// The number will be ASCII. This creates the string using the "input
// iterator" variant which promotes from 8-bit to 16-bit via "=".
return base::string16(&buffer[0], &buffer[strlen(buffer)]);
return base::string16(&buffer[0], &buffer[builder.position()]);
}
bool StringToInt(StringPiece input, int* output) {
......@@ -418,24 +426,24 @@ bool StringToSizeT(StringPiece16 input, size_t* output) {
}
bool StringToDouble(const std::string& input, double* output) {
// Thread-safe? It is on at least Mac, Linux, and Windows.
internal::ScopedClearLastError clear_errno;
static NoDestructor<double_conversion::StringToDoubleConverter> converter(
double_conversion::StringToDoubleConverter::ALLOW_LEADING_SPACES |
double_conversion::StringToDoubleConverter::ALLOW_TRAILING_JUNK,
0.0, 0, nullptr, nullptr);
char* endptr = nullptr;
*output = dmg_fp::strtod(input.c_str(), &endptr);
int processed_characters_count;
*output = converter->StringToDouble(input.c_str(), input.size(),
&processed_characters_count);
// Cases to return false:
// - If errno is ERANGE, there was an overflow or underflow.
// - If the input string is empty, there was nothing to parse.
// - If endptr does not point to the end of the string, there are either
// characters remaining in the string after a parsed number, or the string
// does not begin with a parseable number. endptr is compared to the
// expected end given the string's stated length to correctly catch cases
// where the string contains embedded NUL characters.
// - If the value saturated to HUGE_VAL.
// - If the entire string was not processed, there are either characters
// remaining in the string after a parsed number, or the string does not
// begin with a parseable number.
// - If the first character is a space, there was leading whitespace
return errno == 0 &&
!input.empty() &&
input.c_str() + input.length() == endptr &&
return !input.empty() && *output != HUGE_VAL && *output != -HUGE_VAL &&
static_cast<size_t>(processed_characters_count) == input.size() &&
!isspace(input[0]);
}
......
......@@ -791,6 +791,8 @@ TEST(StringNumberConversionsTest, StringToDouble) {
};
for (size_t i = 0; i < base::size(cases); ++i) {
SCOPED_TRACE(
StringPrintf("case %" PRIuS " \"%s\"", i, cases[i].input.c_str()));
double output;
errno = 1;
EXPECT_EQ(cases[i].success, StringToDouble(cases[i].input, &output));
......@@ -815,6 +817,7 @@ TEST(StringNumberConversionsTest, DoubleToString) {
const char* expected;
} cases[] = {
{0.0, "0"},
{0.5, "0.5"},
{1.25, "1.25"},
{1.33518e+012, "1.33518e+12"},
{1.33489e+012, "1.33489e+12"},
......@@ -832,12 +835,12 @@ TEST(StringNumberConversionsTest, DoubleToString) {
const char input_bytes[8] = {0, 0, 0, 0, '\xee', '\x6d', '\x73', '\x42'};
double input = 0;
memcpy(&input, input_bytes, base::size(input_bytes));
EXPECT_EQ("1335179083776", NumberToString(input));
EXPECT_EQ("1.335179083776e+12", NumberToString(input));
const char input_bytes2[8] =
{0, 0, 0, '\xa0', '\xda', '\x6c', '\x73', '\x42'};
input = 0;
memcpy(&input, input_bytes2, base::size(input_bytes2));
EXPECT_EQ("1334890332160", NumberToString(input));
EXPECT_EQ("1.33489033216e+12", NumberToString(input));
}
TEST(StringNumberConversionsTest, HexEncode) {
......@@ -892,6 +895,7 @@ TEST(StringNumberConversionsTest, StrtodFailures) {
};
for (const auto& test : cases) {
SCOPED_TRACE(StringPrintf("input: \"%s\"", test.input));
double output;
EXPECT_TRUE(StringToDouble(test.input, &output));
EXPECT_EQ(bit_cast<uint64_t>(output), test.expected);
......
/****************************************************************
*
* The author of this software is David M. Gay.
*
* Copyright (c) 1991, 2000, 2001 by Lucent Technologies.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose without fee is hereby granted, provided that this entire notice
* is included in all copies of any software which is or includes a copy
* or modification of this software and in all copies of the supporting
* documentation for such software.
*
* THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTY. IN PARTICULAR, NEITHER THE AUTHOR NOR LUCENT MAKES ANY
* REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
* OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
*
***************************************************************/
Name: David M. Gay's floating point routines
URL: http://www.netlib.org/fp/
License: MIT-like
Original dtoa.c file can be found at <http://www.netlib.org/fp/dtoa.c>.
Original g_fmt.c file can be found at <http://www.netlib.org/fp/g_fmt.c>.
List of changes made to original code:
- wrapped functions in dmg_fp namespace
- renamed .c files to .cc
- added dmg_fp.h header
- added #define IEEE_8087 to dtoa.cc
- added #define NO_HEX_FP to dtoa.cc
- made some minor changes to allow clean compilation under g++ -Wall, see
gcc_warnings.patch.
- made some minor changes to build on 64-bit, see gcc_64_bit.patch.
- made minor changes for -Wextra for Mac build, see mac_wextra.patch
- fixed warnings under msvc, see msvc_warnings.patch
- fixed parsing of long exponents, see exp_length.patch and crbug.com/542881
- made hexdig array const
- removed deprecated `register` keyword
- #undef Long so that it won't change Long in other files in jumbo builds
diff --git a/base/third_party/dmg_fp/dtoa.cc b/base/third_party/dmg_fp/dtoa.cc
index d7e6826..be560bc 100644
--- a/base/third_party/dmg_fp/dtoa.cc
+++ b/base/third_party/dmg_fp/dtoa.cc
@@ -1533,7 +1533,7 @@ hexdig_init(void) /* Use of hexdig_init omitted 20121220 to avoid a */
htinit(hexdig, USC "ABCDEF", 0x10 + 10);
}
#else
-static unsigned char hexdig[256] = {
+static const unsigned char hexdig[256] = {
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
// Copyright (c) 2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef THIRD_PARTY_DMG_FP_H_
#define THIRD_PARTY_DMG_FP_H_
namespace dmg_fp {
// Return a nearest machine number to the input decimal
// string (or set errno to ERANGE). With IEEE arithmetic, ties are
// broken by the IEEE round-even rule. Otherwise ties are broken by
// biased rounding (add half and chop).
double strtod(const char* s00, char** se);
// Convert double to ASCII string. For meaning of parameters
// see dtoa.cc file.
char* dtoa(double d, int mode, int ndigits,
int* decpt, int* sign, char** rve);
// Must be used to free values returned by dtoa.
void freedtoa(char* s);
// Store the closest decimal approximation to x in b (null terminated).
// Returns a pointer to b. It is sufficient for |b| to be 32 characters.
char* g_fmt(char* b, double x);
} // namespace dmg_fp
#endif // THIRD_PARTY_DMG_FP_H_
This diff is collapsed.
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// The purpose of this file is to supply the macro definintions necessary
// to make third_party/dmg_fp/dtoa.cc threadsafe.
#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/synchronization/lock.h"
// We need two locks because they're sometimes grabbed at the same time.
// A single lock would lead to an attempted recursive grab.
static base::LazyInstance<base::Lock>::Leaky
dtoa_lock_0 = LAZY_INSTANCE_INITIALIZER;
static base::LazyInstance<base::Lock>::Leaky
dtoa_lock_1 = LAZY_INSTANCE_INITIALIZER;
/*
* This define and the code below is to trigger thread-safe behavior
* in dtoa.cc, per this comment from the file:
*
* #define MULTIPLE_THREADS if the system offers preemptively scheduled
* multiple threads. In this case, you must provide (or suitably
* #define) two locks, acquired by ACQUIRE_DTOA_LOCK(n) and freed
* by FREE_DTOA_LOCK(n) for n = 0 or 1. (The second lock, accessed
* in pow5mult, ensures lazy evaluation of only one copy of high
* powers of 5; omitting this lock would introduce a small
* probability of wasting memory, but would otherwise be harmless.)
* You must also invoke freedtoa(s) to free the value s returned by
* dtoa. You may do so whether or not MULTIPLE_THREADS is #defined.
*/
#define MULTIPLE_THREADS
inline static void ACQUIRE_DTOA_LOCK(size_t n) {
DCHECK(n < 2);
base::Lock* lock = n == 0 ? dtoa_lock_0.Pointer() : dtoa_lock_1.Pointer();
lock->Acquire();
}
inline static void FREE_DTOA_LOCK(size_t n) {
DCHECK(n < 2);
base::Lock* lock = n == 0 ? dtoa_lock_0.Pointer() : dtoa_lock_1.Pointer();
lock->Release();
}
#include "base/third_party/dmg_fp/dtoa.cc"
#undef Bias // Avoid windows jumbo build breakage.
#undef Long // To avoid breaking jni code in jumbo builds
diff --git a/base/third_party/dmg_fp/dtoa.cc b/base/third_party/dmg_fp/dtoa.cc
index c0a51c2..ab4e056 100644
--- a/base/third_party/dmg_fp/dtoa.cc
+++ b/base/third_party/dmg_fp/dtoa.cc
@@ -2674,8 +2674,11 @@ strtod
if (c > '0' && c <= '9') {
L = c - '0';
s1 = s;
- while((c = *++s) >= '0' && c <= '9')
- L = 10*L + c - '0';
+ while((c = *++s) >= '0' && c <= '9') {
+ if (L < (INT_MAX - 10) / 10) {
+ L = 10*L + (c - '0');
+ }
+ }
if (s - s1 > 8 || L > 19999)
/* Avoid confusion from exponents
* so large that e might overflow.
/****************************************************************
*
* The author of this software is David M. Gay.
*
* Copyright (c) 1991, 1996 by Lucent Technologies.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose without fee is hereby granted, provided that this entire notice
* is included in all copies of any software which is or includes a copy
* or modification of this software and in all copies of the supporting
* documentation for such software.
*
* THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTY. IN PARTICULAR, NEITHER THE AUTHOR NOR LUCENT MAKES ANY
* REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
* OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
*
***************************************************************/
/* g_fmt(buf,x) stores the closest decimal approximation to x in buf;
* it suffices to declare buf
* char buf[32];
*/
#include "dmg_fp.h"
namespace dmg_fp {
char *
g_fmt(char *b, double x)
{
int i, k;
char *s;
int decpt, j, sign;
char *b0, *s0, *se;
b0 = b;
#ifdef IGNORE_ZERO_SIGN
if (!x) {
*b++ = '0';
*b = 0;
goto done;
}
#endif
s = s0 = dtoa(x, 0, 0, &decpt, &sign, &se);
if (sign)
*b++ = '-';
if (decpt == 9999) /* Infinity or Nan */ {
for(*b = *s++; *b++; *b = *s++) {}
goto done0;
}
if (decpt <= -4 || decpt > se - s + 5) {
*b++ = *s++;
if (*s) {
*b++ = '.';
for(*b = *s++; *b; *b = *s++)
b++;
}
*b++ = 'e';
/* sprintf(b, "%+.2d", decpt - 1); */
if (--decpt < 0) {
*b++ = '-';
decpt = -decpt;
}
else
*b++ = '+';
for(j = 2, k = 10; 10*k <= decpt; j++, k *= 10) {}
for(;;) {
i = decpt / k;
*b++ = (char)i + '0';
if (--j <= 0)
break;
decpt -= i*k;
decpt *= 10;
}
*b = 0;
}
else if (decpt <= 0) {
*b++ = '.';
for(; decpt < 0; decpt++)
*b++ = '0';
for(*b = *s++; *b++; *b = *s++) {}
}
else {
for(*b = *s++; *b; *b = *s++) {
b++;
if (--decpt == 0 && *s)
*b++ = '.';
}
for(; decpt > 0; decpt--)
*b++ = '0';
*b = 0;
}
done0:
freedtoa(s0);
#ifdef IGNORE_ZERO_SIGN
done:
#endif
return b0;
}
} // namespace dmg_fp
Index: dtoa.cc
--- dtoa.cc (old copy)
+++ dtoa.cc (working copy)
@@ -183,8 +183,12 @@
#define NO_HEX_FP
#ifndef Long
+#if __LP64__
+#define Long int
+#else
#define Long long
#endif
+#endif
#ifndef ULong
typedef unsigned Long ULong;
#endif
@@ -221,7 +225,7 @@ extern void *MALLOC(size_t);
#ifndef PRIVATE_MEM
#define PRIVATE_MEM 2304
#endif
-#define PRIVATE_mem ((PRIVATE_MEM+sizeof(double)-1)/sizeof(double))
+#define PRIVATE_mem ((unsigned)((PRIVATE_MEM+sizeof(double)-1)/sizeof(double)))
static double private_mem[PRIVATE_mem], *pmem_next = private_mem;
#endif
Index: dtoa.cc
--- dtoa.cc (old copy)
+++ dtoa.cc (working copy)
@@ -179,6 +179,9 @@
* used for input more than STRTOD_DIGLIM digits long (default 40).
*/
+#define IEEE_8087
+#define NO_HEX_FP
+
#ifndef Long
#define Long long
#endif
@@ -280,9 +283,7 @@
#include "math.h"
#endif
-#ifdef __cplusplus
-extern "C" {
-#endif
+namespace dmg_fp {
#ifndef CONST
#ifdef KR_headers
@@ -511,11 +512,9 @@
#define Kmax 7
-#ifdef __cplusplus
-extern "C" double strtod(const char *s00, char **se);
-extern "C" char *dtoa(double d, int mode, int ndigits,
+double strtod(const char *s00, char **se);
+char *dtoa(double d, int mode, int ndigits,
int *decpt, int *sign, char **rve);
-#endif
struct
Bigint {
@@ -1527,7 +1526,7 @@
#ifdef KR_headers
(sp, t) char **sp, *t;
#else
- (CONST char **sp, char *t)
+ (CONST char **sp, CONST char *t)
#endif
{
int c, d;
@@ -2234,7 +2234,7 @@ bigcomp
nd = bc->nd;
nd0 = bc->nd0;
p5 = nd + bc->e0 - 1;
- speccase = 0;
+ dd = speccase = 0;
#ifndef Sudden_Underflow
if (rv->d == 0.) { /* special case: value near underflow-to-zero */
/* threshold was rounded to zero */
@@ -3431,7 +3430,7 @@
j = sizeof(ULong);
for(k = 0;
- sizeof(Bigint) - sizeof(ULong) - sizeof(int) + j <= i;
+ sizeof(Bigint) - sizeof(ULong) - sizeof(int) + j <= (size_t)i;
j <<= 1)
k++;
r = (int*)Balloc(k);
@@ -3447,7 +3446,7 @@
#ifdef KR_headers
nrv_alloc(s, rve, n) char *s, **rve; int n;
#else
-nrv_alloc(char *s, char **rve, int n)
+nrv_alloc(CONST char *s, char **rve, int n)
#endif
{
char *rv, *t;
@@ -4202,6 +4201,5 @@
*rve = s;
return s0;
}
-#ifdef __cplusplus
-}
-#endif
+
+} // namespace dmg_fp
Index: g_fmt.cc
--- g_fmt.cc (old copy)
+++ g_fmt.cc (new copy)
@@ -46,14 +46,14 @@ g_fmt(register char *b, double x)
if (sign)
*b++ = '-';
if (decpt == 9999) /* Infinity or Nan */ {
- while(*b++ = *s++);
+ while((*b++ = *s++));
goto done0;
}
if (decpt <= -4 || decpt > se - s + 5) {
*b++ = *s++;
if (*s) {
*b++ = '.';
- while(*b = *s++)
+ while((*b = *s++))
b++;
}
*b++ = 'e';
@@ -79,10 +79,10 @@ g_fmt(register char *b, double x)
*b++ = '.';
for(; decpt < 0; decpt++)
*b++ = '0';
- while(*b++ = *s++);
+ while((*b++ = *s++));
}
else {
- while(*b = *s++) {
+ while((*b = *s++)) {
b++;
if (--decpt == 0 && *s)
*b++ = '.';
@@ -93,7 +93,9 @@ g_fmt(register char *b, double x)
}
done0:
freedtoa(s0);
+#ifdef IGNORE_ZERO_SIGN
done:
+#endif
return b0;
}
This diff is collapsed.
This diff is collapsed.
# Copyright 2019 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
config("config") {
visibility = [ ":double_conversion" ]
cflags = [
"-Wno-unused-const-variable",
"-Wno-unused-function",
]
}
static_library("double_conversion") {
sources = [
"double-conversion/bignum-dtoa.cc",
"double-conversion/bignum-dtoa.h",
"double-conversion/bignum.cc",
"double-conversion/bignum.h",
"double-conversion/cached-powers.cc",
"double-conversion/cached-powers.h",
"double-conversion/diy-fp.h",
"double-conversion/double-conversion.h",
"double-conversion/double-to-string.cc",
"double-conversion/double-to-string.h",
"double-conversion/fast-dtoa.cc",
"double-conversion/fast-dtoa.h",
"double-conversion/fixed-dtoa.cc",
"double-conversion/fixed-dtoa.h",
"double-conversion/ieee.h",
"double-conversion/string-to-double.cc",
"double-conversion/string-to-double.h",
"double-conversion/strtod.cc",
"double-conversion/strtod.h",
"double-conversion/utils.h",
]
configs += [ ":config" ]
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -196,7 +196,7 @@ TEST(ColorUtils, AlphaBlend) {
TEST(ColorUtils, SkColorToRgbaString) {
SkColor color = SkColorSetARGB(153, 100, 150, 200);
std::string color_string = SkColorToRgbaString(color);
EXPECT_EQ(color_string, "rgba(100,150,200,.6)");
EXPECT_EQ(color_string, "rgba(100,150,200,0.6)");
}
TEST(ColorUtils, SkColorToRgbString) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment