diff options
Diffstat (limited to 'base')
-rw-r--r-- | base/base.xcodeproj/project.pbxproj | 16 | ||||
-rw-r--r-- | base/build/base.vcproj | 4 | ||||
-rw-r--r-- | base/float_util.h | 50 | ||||
-rw-r--r-- | base/json_reader.cc | 44 | ||||
-rw-r--r-- | base/json_reader_unittest.cc | 23 | ||||
-rw-r--r-- | base/string_util.cc | 58 | ||||
-rw-r--r-- | base/string_util.h | 10 | ||||
-rw-r--r-- | base/string_util_unittest.cc | 63 |
8 files changed, 214 insertions, 54 deletions
diff --git a/base/base.xcodeproj/project.pbxproj b/base/base.xcodeproj/project.pbxproj index 75041cc..a27f5c5 100644 --- a/base/base.xcodeproj/project.pbxproj +++ b/base/base.xcodeproj/project.pbxproj @@ -23,6 +23,8 @@ /* End PBXAggregateTarget section */ /* Begin PBXBuildFile section */ + 7B4C5D840E49157E00679E8F /* json_reader_unittest.cc in Sources */ = {isa = PBXBuildFile; fileRef = E4562C3B0E28017F005E4685 /* json_reader_unittest.cc */; }; + 7B4C5D890E4915D800679E8F /* float_util.h in Headers */ = {isa = PBXBuildFile; fileRef = 7B4C5D880E4915D800679E8F /* float_util.h */; }; 820EB4F00E3A610A009668FC /* linked_ptr.h in Headers */ = {isa = PBXBuildFile; fileRef = 820EB4EF0E3A610A009668FC /* linked_ptr.h */; }; 820EB4F70E3A613F009668FC /* string_piece.cc in Sources */ = {isa = PBXBuildFile; fileRef = 820EB4F50E3A613F009668FC /* string_piece.cc */; }; 820EB4F80E3A613F009668FC /* string_piece.h in Headers */ = {isa = PBXBuildFile; fileRef = 820EB4F60E3A613F009668FC /* string_piece.h */; }; @@ -294,6 +296,7 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 7B4C5D880E4915D800679E8F /* float_util.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = float_util.h; sourceTree = "<group>"; }; 7B5AD60D0D9DD8050012BCF1 /* scoped_cftyperef.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = scoped_cftyperef.h; sourceTree = "<group>"; }; 7BD9E84E0DA447F800FC7A01 /* singleton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = singleton.h; sourceTree = "<group>"; }; 7BEB81100D9AD288009BA8DD /* prcpucfg_mac.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = prcpucfg_mac.h; path = third_party/nspr/prcpucfg_mac.h; sourceTree = "<group>"; }; @@ -571,6 +574,7 @@ 8254030B0D92D1D10006B936 /* fix_wp64.h */, 8254030D0D92D1DB0006B936 /* fixed_string.h */, E4562C310E2800DE005E4685 /* fixed_string_unittest.cc */, + 7B4C5D880E4915D800679E8F /* float_util.h */, 824652C00DC12044007C2BAA /* hash_tables.h */, 8254030F0D92D1E80006B936 /* iat_patch.cc */, 825403100D92D1E80006B936 /* iat_patch.h */, @@ -841,6 +845,7 @@ E48A066B0E3F70B500172919 /* convolver.h in Headers */, E49357230E422A38008F8B09 /* timer.h in Headers */, E49115EE0E47B461001EE8C3 /* at_exit.h in Headers */, + 7B4C5D890E4915D800679E8F /* float_util.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1149,17 +1154,18 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - E45A2C260E47ACC300DB1196 /* string_util.cc in Sources */, - E45A2C7C0E47B20B00DB1196 /* string_util_mac.cc in Sources */, - E45A2C7D0E47B20E00DB1196 /* string_util_icu.cc in Sources */, E49115ED0E47B461001EE8C3 /* at_exit.cc in Sources */, E49115F40E47B496001EE8C3 /* at_exit_unittest.cc in Sources */, - E49115F90E47B4CB001EE8C3 /* logging.cc in Sources */, + E49115FD0E47B502001EE8C3 /* base_switches.cc in Sources */, E49115FA0E47B4D3001EE8C3 /* command_line.cc in Sources */, E49115FB0E47B4D3001EE8C3 /* command_line_unittest.cc in Sources */, - E49115FD0E47B502001EE8C3 /* base_switches.cc in Sources */, E491162D0E4895E2001EE8C3 /* fixed_string_unittest.cc in Sources */, + 7B4C5D840E49157E00679E8F /* json_reader_unittest.cc in Sources */, + E49115F90E47B4CB001EE8C3 /* logging.cc in Sources */, E491165D0E48A51E001EE8C3 /* stack_container_unittest.cc in Sources */, + E45A2C260E47ACC300DB1196 /* string_util.cc in Sources */, + E45A2C7D0E47B20E00DB1196 /* string_util_icu.cc in Sources */, + E45A2C7C0E47B20B00DB1196 /* string_util_mac.cc in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/base/build/base.vcproj b/base/build/base.vcproj index c76886f..7a45722 100644 --- a/base/build/base.vcproj +++ b/base/build/base.vcproj @@ -294,6 +294,10 @@ > </File> <File + RelativePath="..\float_util.h" + > + </File> + <File RelativePath="..\histogram.cc" > </File> diff --git a/base/float_util.h b/base/float_util.h new file mode 100644 index 0000000..6047417 --- /dev/null +++ b/base/float_util.h @@ -0,0 +1,50 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef BASE_FLOAT_UTIL_H_ +#define BASE_FLOAT_UTIL_H_ + +#include "build/build_config.h" + +#include <float.h> +#include <math.h> + +namespace base { + +inline bool IsFinite(const double& number) { +#if defined(OS_POSIX) + return finite(number) != 0; +#elif defined(OS_WIN) + return _finite(number) != 0; +#endif +} + +} // namespace base + +#endif // BASE_FLOAT_UTIL_H_ diff --git a/base/json_reader.cc b/base/json_reader.cc index 257f635..66d8f68 100644 --- a/base/json_reader.cc +++ b/base/json_reader.cc @@ -29,8 +29,7 @@ #include "base/json_reader.h" -#include <float.h> - +#include "base/float_util.h" #include "base/logging.h" #include "base/string_util.h" #include "base/values.h" @@ -343,39 +342,20 @@ JSONReader::Token JSONReader::ParseNumberToken() { } bool JSONReader::DecodeNumber(const Token& token, Value** node) { - // Determine if we want to try to parse as an int or a double. - bool is_double = false; - for (int i = 0; i < token.length; ++i) { - wchar_t c = *(token.begin + i); - if ('e' == c || 'E' == c || '.' == c) { - is_double = true; - break; - } + const std::wstring num_string(token.begin, token.length); + + int num_int; + if (StringToInt(num_string, &num_int)) { + *node = Value::CreateIntegerValue(num_int); + return true; } - if (is_double) { - // Try parsing as a double. - double num_double; - int parsed_values = swscanf_s(token.begin, L"%lf", &num_double); - // Make sure we're not -INF, INF or NAN. - if (1 == parsed_values && _finite(num_double)) { - *node = Value::CreateRealValue(num_double); - return true; - } - } else { - int num_int; - int parsed_values = swscanf_s(token.begin, L"%d", &num_int); - if (1 == parsed_values) { - // Ensure the parsed value matches the string. This makes sure we don't - // overflow/underflow. - const std::wstring& back_to_str = StringPrintf(L"%d", num_int); - if (0 == wcsncmp(back_to_str.c_str(), token.begin, - back_to_str.length())) { - *node = Value::CreateIntegerValue(num_int); - return true; - } - } + double num_double; + if (StringToDouble(num_string, &num_double) && base::IsFinite(num_double)) { + *node = Value::CreateRealValue(num_double); + return true; } + return false; } diff --git a/base/json_reader_unittest.cc b/base/json_reader_unittest.cc index 130c8e8..1a81425 100644 --- a/base/json_reader_unittest.cc +++ b/base/json_reader_unittest.cc @@ -83,13 +83,14 @@ TEST(JSONReaderTest, Reading) { ASSERT_EQ(0, int_val); delete root; - // Numbers that overflow ints should fail + // Numbers that overflow ints should succeed, being internally promoted to + // storage as doubles root = NULL; - ASSERT_FALSE(JSONReader::JsonToValue("2147483648", &root, false, false)); - ASSERT_FALSE(root); + ASSERT_TRUE(JSONReader::JsonToValue("2147483648", &root, false, false)); + ASSERT_TRUE(root); root = NULL; - ASSERT_FALSE(JSONReader::JsonToValue("-2147483649", &root, false, false)); - ASSERT_FALSE(root); + ASSERT_TRUE(JSONReader::JsonToValue("-2147483649", &root, false, false)); + ASSERT_TRUE(root); // Parse a double root = NULL; @@ -264,7 +265,7 @@ TEST(JSONReaderTest, Reading) { ASSERT_TRUE(root); ASSERT_TRUE(root->IsType(Value::TYPE_LIST)); ListValue* list = static_cast<ListValue*>(root); - ASSERT_EQ(3, list->GetSize()); + ASSERT_EQ(static_cast<size_t>(3), list->GetSize()); // Test with trailing comma. Should be parsed the same as above. Value* root2 = NULL; @@ -279,7 +280,7 @@ TEST(JSONReaderTest, Reading) { ASSERT_TRUE(root); ASSERT_TRUE(root->IsType(Value::TYPE_LIST)); list = static_cast<ListValue*>(root); - ASSERT_EQ(0, list->GetSize()); + ASSERT_EQ(static_cast<size_t>(0), list->GetSize()); delete root; // Nested arrays @@ -289,7 +290,7 @@ TEST(JSONReaderTest, Reading) { ASSERT_TRUE(root); ASSERT_TRUE(root->IsType(Value::TYPE_LIST)); list = static_cast<ListValue*>(root); - ASSERT_EQ(4, list->GetSize()); + ASSERT_EQ(static_cast<size_t>(4), list->GetSize()); // Lots of trailing commas. root2 = NULL; @@ -327,7 +328,7 @@ TEST(JSONReaderTest, Reading) { ASSERT_TRUE(root); ASSERT_TRUE(root->IsType(Value::TYPE_LIST)); list = static_cast<ListValue*>(root); - EXPECT_EQ(1, list->GetSize()); + EXPECT_EQ(static_cast<size_t>(1), list->GetSize()); Value* tmp_value = NULL; ASSERT_TRUE(list->Get(0, &tmp_value)); EXPECT_TRUE(tmp_value->IsType(Value::TYPE_BOOLEAN)); @@ -391,7 +392,7 @@ TEST(JSONReaderTest, Reading) { ASSERT_TRUE(dict_val->GetDictionary(L"inner", &inner_dict)); ListValue* inner_array = NULL; ASSERT_TRUE(inner_dict->GetList(L"array", &inner_array)); - ASSERT_EQ(1, inner_array->GetSize()); + ASSERT_EQ(static_cast<size_t>(1), inner_array->GetSize()); bool_value = true; ASSERT_TRUE(dict_val->GetBoolean(L"false", &bool_value)); ASSERT_FALSE(bool_value); @@ -465,7 +466,7 @@ TEST(JSONReaderTest, Reading) { ASSERT_TRUE(root); ASSERT_TRUE(root->IsType(Value::TYPE_LIST)); list = static_cast<ListValue*>(root); - ASSERT_EQ(5001, list->GetSize()); + ASSERT_EQ(static_cast<size_t>(5001), list->GetSize()); delete root; // Test utf8 encoded input diff --git a/base/string_util.cc b/base/string_util.cc index 3c7bb87..4f095b7 100644 --- a/base/string_util.cc +++ b/base/string_util.cc @@ -144,7 +144,7 @@ class StringToLongTraits { return strtol(str, endptr, kBase); } static inline bool valid_func(const string_type& str) { - return !isspace(str[0]); + return !str.empty() && !isspace(str[0]); } }; @@ -158,7 +158,7 @@ class WStringToLongTraits { return wcstol(str, endptr, kBase); } static inline bool valid_func(const string_type& str) { - return !iswspace(str[0]); + return !str.empty() && !iswspace(str[0]); } }; @@ -176,7 +176,7 @@ class StringToInt64Traits { #endif } static inline bool valid_func(const string_type& str) { - return !isspace(str[0]); + return !str.empty() && !isspace(str[0]); } }; @@ -194,7 +194,7 @@ class WStringToInt64Traits { #endif } static inline bool valid_func(const string_type& str) { - return !iswspace(str[0]); + return !str.empty() && !iswspace(str[0]); } }; @@ -211,7 +211,7 @@ class HexStringToLongTraits { return strtoul(str, endptr, kBase); } static inline bool valid_func(const string_type& str) { - return !isspace(str[0]); + return !str.empty() && !isspace(str[0]); } }; @@ -225,7 +225,33 @@ class HexWStringToLongTraits { return wcstoul(str, endptr, kBase); } static inline bool valid_func(const string_type& str) { - return !iswspace(str[0]); + return !str.empty() && !iswspace(str[0]); + } +}; + +class StringToDoubleTraits { + public: + typedef std::string string_type; + typedef double value_type; + static inline value_type convert_func(const string_type::value_type* str, + string_type::value_type** endptr) { + return strtod(str, endptr); + } + static inline bool valid_func(const string_type& str) { + return !str.empty() && !isspace(str[0]); + } +}; + +class WStringToDoubleTraits { + public: + typedef std::wstring string_type; + typedef double value_type; + static inline value_type convert_func(const string_type::value_type* str, + string_type::value_type** endptr) { + return wcstod(str, endptr); + } + static inline bool valid_func(const string_type& str) { + return !str.empty() && !iswspace(str[0]); } }; @@ -1197,6 +1223,14 @@ bool HexStringToInt(const std::wstring& input, int* output) { input, reinterpret_cast<long*>(output)); } +bool StringToDouble(const std::string& input, double* output) { + return StringToNumber<StringToDoubleTraits>(input, output); +} + +bool StringToDouble(const std::wstring& input, double* output) { + return StringToNumber<WStringToDoubleTraits>(input, output); +} + int StringToInt(const std::string& value) { int result; StringToInt(value, &result); @@ -1232,3 +1266,15 @@ int HexStringToInt(const std::wstring& value) { HexStringToInt(value, &result); return result; } + +double StringToDouble(const std::string& value) { + double result; + StringToDouble(value, &result); + return result; +} + +double StringToDouble(const std::wstring& value) { + double result; + StringToDouble(value, &result); + return result; +} diff --git a/base/string_util.h b/base/string_util.h index 129f124..b89694f 100644 --- a/base/string_util.h +++ b/base/string_util.h @@ -342,6 +342,14 @@ bool StringToInt64(const std::wstring& input, int64* output); bool HexStringToInt(const std::string& input, int* output); bool HexStringToInt(const std::wstring& input, int* output); +// For floating-point conversions, only conversions of input strings in decimal +// form are defined to work. Behavior with strings representing floating-point +// numbers in hexadecimal, and strings representing non-fininte values (such +// as NaN and inf) is undefined. Otherwise, these behave the same as the +// integral variants above. +bool StringToDouble(const std::string& input, double* output); +bool StringToDouble(const std::wstring& input, double* output); + // Convenience forms of the above, when the caller is uninterested in the // boolean return value. These return only the |*output| value from the // above conversions: a best-effort conversion when possible, otherwise, 0. @@ -351,6 +359,8 @@ int64 StringToInt64(const std::string& value); int64 StringToInt64(const std::wstring& value); int HexStringToInt(const std::string& value); int HexStringToInt(const std::wstring& value); +double StringToDouble(const std::string& value); +double StringToDouble(const std::wstring& value); // Return a C++ string given printf-like input. std::string StringPrintf(const char* format, ...); diff --git a/base/string_util_unittest.cc b/base/string_util_unittest.cc index 1aa4043..dcebddf 100644 --- a/base/string_util_unittest.cc +++ b/base/string_util_unittest.cc @@ -735,6 +735,8 @@ TEST(StringUtilTest, StringToInt64) { {"99999999999", GG_INT64_C(99999999999), true}, {"9223372036854775807", kint64max, true}, {"-9223372036854775808", kint64min, true}, + {"09", 9, true}, + {"-09", -9, true}, {"", 0, false}, {" 42", 42, false}, {"\t\n\v\f\r 42", 42, false}, @@ -801,6 +803,8 @@ TEST(StringUtilTest, HexStringToInt) { {"0x80000000", INT_MIN, true}, {"0xffffffff", -1, true}, {"0XDeadBeef", 0xdeadbeef, true}, + {"0x0f", 15, true}, + {"0f", 15, true}, {" 45", 0x45, false}, {"\t\n\v\f\r 0x45", 0x45, false}, {"efgh", 0xef, false}, @@ -836,6 +840,65 @@ TEST(StringUtilTest, HexStringToInt) { EXPECT_EQ(0xc0ffee, output); } +TEST(StringUtilTest, StringToDouble) { + static const struct { + std::string input; + double output; + bool success; + } cases[] = { + {"0", 0.0, true}, + {"42", 42.0, true}, + {"-42", -42.0, true}, + {"123.45", 123.45, true}, + {"-123.45", -123.45, true}, + {"+123.45", 123.45, true}, + {"2.99792458e8", 299792458.0, true}, + {"149597870.691E+3", 149597870691.0, true}, + {"6.", 6.0, true}, + {"9e99999999999999999999", HUGE_VAL, false}, + {"-9e99999999999999999999", -HUGE_VAL, false}, + {"1e-2", 0.01, true}, + {"-1E-7", -0.0000001, true}, + {"01e02", 100, true}, + {"2.3e15", 2.3e15, true}, + {"\t\n\v\f\r -123.45e2", -12345.0, false}, + {"+123 e4", 123.0, false}, + {"123e ", 123.0, false}, + {"123e", 123.0, false}, + {" 2.99", 2.99, false}, + {"1e3.4", 1000.0, false}, + {"nothing", 0.0, false}, + {"-", 0.0, false}, + {"+", 0.0, false}, + {"", 0.0, false}, + }; + + for (int i = 0; i < arraysize(cases); ++i) { + EXPECT_DOUBLE_EQ(cases[i].output, StringToDouble(cases[i].input)); + double output; + EXPECT_EQ(cases[i].success, StringToDouble(cases[i].input, &output)); + EXPECT_DOUBLE_EQ(cases[i].output, output); + + std::wstring wide_input = ASCIIToWide(cases[i].input); + EXPECT_DOUBLE_EQ(cases[i].output, StringToDouble(wide_input)); + EXPECT_EQ(cases[i].success, StringToDouble(wide_input, &output)); + EXPECT_DOUBLE_EQ(cases[i].output, output); + } + + // One additional test to verify that conversion of numbers in strings with + // embedded NUL characters. The NUL and extra data after it should be + // interpreted as junk after the number. + const char input[] = "3.14\0159"; + std::string input_string(input, arraysize(input) - 1); + double output; + EXPECT_FALSE(StringToDouble(input_string, &output)); + EXPECT_DOUBLE_EQ(3.14, output); + + std::wstring wide_input = ASCIIToWide(input_string); + EXPECT_FALSE(StringToDouble(wide_input, &output)); + EXPECT_DOUBLE_EQ(3.14, output); +} + // This checks where we can use the assignment operator for a va_list. We need // a way to do this since Visual C doesn't support va_copy, but assignment on // va_list is not guaranteed to be a copy. See StringAppendVT which uses this |