diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/common/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/common/String.cpp | 19 | ||||
-rw-r--r-- | src/common/StringToNumberConverter.cpp | 154 | ||||
-rw-r--r-- | src/platform/graphics/Geometry.cpp | 2 |
4 files changed, 166 insertions, 10 deletions
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index ae418f6b..8bfcb867 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -4,6 +4,7 @@ add_library(cru_base SHARED Format.cpp PropertyTree.cpp String.cpp + StringToNumberConverter.cpp StringUtil.cpp io/Stream.cpp io/Resource.cpp diff --git a/src/common/String.cpp b/src/common/String.cpp index ed66b914..908f64b6 100644 --- a/src/common/String.cpp +++ b/src/common/String.cpp @@ -313,12 +313,13 @@ Range String::RangeFromCodePointToCodeUnit(Range code_point_range) const { return View().RangeFromCodePointToCodeUnit(code_point_range); } -float String::ParseToFloat(Index* processed_characters_count, int flags) const { +float String::ParseToFloat(Index* processed_characters_count, + unsigned flags) const { return View().ParseToFloat(processed_characters_count, flags); } double String::ParseToDouble(Index* processed_characters_count, - int flags) const { + unsigned flags) const { return View().ParseToDouble(processed_characters_count, flags); } @@ -516,15 +517,15 @@ std::string StringView::ToUtf8() const { return result; } -static int MapStringToFloatFlags(int flags) { +static int MapStringToDoubleFlags(int flags) { int f = double_conversion::StringToDoubleConverter::ALLOW_CASE_INSENSIBILITY; - if (flags & StringToFloatFlags::kAllowLeadingSpaces) { + if (flags & StringToNumberFlags::kAllowLeadingSpaces) { f |= double_conversion::StringToDoubleConverter::ALLOW_LEADING_SPACES; } - if (flags & StringToFloatFlags::kAllowTrailingSpaces) { + if (flags & StringToNumberFlags::kAllowTrailingSpaces) { f |= double_conversion::StringToDoubleConverter::ALLOW_TRAILING_SPACES; } - if (flags & StringToFloatFlags::kAllowTrailingJunk) { + if (flags & StringToNumberFlags::kAllowTrailingJunk) { f |= double_conversion::StringToDoubleConverter::ALLOW_TRAILING_JUNK; } return f; @@ -532,11 +533,11 @@ static int MapStringToFloatFlags(int flags) { static double_conversion::StringToDoubleConverter CreateStringToDoubleConverter( int flags) { - return {MapStringToFloatFlags(flags), 0.0, NAN, "inf", "nan"}; + return {MapStringToDoubleFlags(flags), 0.0, NAN, "inf", "nan"}; } float StringView::ParseToFloat(Index* processed_characters_count, - int flags) const { + unsigned flags) const { int pcc; auto result = CreateStringToDoubleConverter(flags).StringToFloat( reinterpret_cast<const uc16*>(ptr_), static_cast<int>(size_), &pcc); @@ -547,7 +548,7 @@ float StringView::ParseToFloat(Index* processed_characters_count, } double StringView::ParseToDouble(Index* processed_characters_count, - int flags) const { + unsigned flags) const { int pcc; auto result = CreateStringToDoubleConverter(flags).StringToDouble( reinterpret_cast<const uc16*>(ptr_), static_cast<int>(size_), &pcc); diff --git a/src/common/StringToNumberConverter.cpp b/src/common/StringToNumberConverter.cpp new file mode 100644 index 00000000..259e39b0 --- /dev/null +++ b/src/common/StringToNumberConverter.cpp @@ -0,0 +1,154 @@ +#include "cru/common/StringToNumberConverter.h" +#include "cru/common/Exception.h" + +namespace cru { +bool StringToIntegerConverterImpl::CheckParams() const { + if (base == 0) { + if (flags & StringToNumberFlags::kAllowLeadingZeroForInteger) { + return false; + } else { + return true; + } + } + + return base >= 2 & base <= 36; +} + +static bool IsSpace(char c) { + return c == ' ' || c == '\t' || c == '\n' || c == '\r'; +} + +StringToIntegerConverterImplResult StringToIntegerConverterImpl::Parse( + const char* const str, const Index size, + Index* processed_characters_count) const { + if (str == nullptr) throw std::invalid_argument("Invalid str."); + if (size < 0) throw std::invalid_argument("Invalid size."); + if (!CheckParams()) throw std::invalid_argument("Invalid parsing flags."); + + const bool throw_on_error = (flags & StringToNumberFlags::kThrowOnError) != 0; + + const char* const end = str + size; + + const char* current = str; + + if (flags & StringToNumberFlags::kAllowLeadingSpaces) { + while (current != end && IsSpace(*current)) { + current++; + } + } + + if (current == end) { + if (processed_characters_count) { + *processed_characters_count = 0; + } + if (throw_on_error) { + throw Exception(u"Empty string (after reading leading spaces)."); + } else { + return {false, 0}; + } + } + + bool negate = false; + + if (*current == '-') { + ++current; + negate = true; + } else if (*current == '+') { + ++current; + } + + if (current == end) { + if (processed_characters_count) { + *processed_characters_count = 0; + } + if (throw_on_error) { + throw Exception(u"Empty string (after reading sign)."); + } else { + return {false, 0}; + } + } + + int base = this->base; + + if (base == 0) { + if (*current == '0') { + ++current; + if (current == end) { + return {negate, 0}; + } else if (*current == 'x' || *current == 'X') { + ++current; + base = 16; + } else if (*current == 'b' || *current == 'B') { + ++current; + base = 2; + } else { + base = 8; + } + } + } + + if (current == end) { + if (processed_characters_count) { + *processed_characters_count = 0; + } + if (throw_on_error) { + throw Exception(u"Empty string (after reading head base indicator)."); + } else { + return {false, 0}; + } + } + + const bool allow_leading_zero = + flags & StringToNumberFlags::kAllowLeadingZeroForInteger; + + while (current != end && *current == '0') { + current++; + } + + if (current == end) { + return {negate, 0}; + } + + const bool allow_trailing_junk = + flags & StringToNumberFlags::kAllowTrailingJunk; + const bool allow_trailing_spaces = + flags & StringToNumberFlags::kAllowTrailingSpaces; + unsigned long long result = 0; + + while (current != end) { + const char c = *current; + if (c >= '0' && c <= (base > 10 ? '9' : base + '0' - 1)) { + result = result * base + c - '0'; + } else if (base > 10 && c >= 'a' && c <= 'z') { + result = result * base + c - 'a' + 10; + } else if (base > 10 && c >= 'A' && c <= 'Z') { + result = result * base + c - 'A' + 10; + } else if (allow_trailing_junk) { + break; + } else if (allow_trailing_spaces && IsSpace(c)) { + break; + } else { + if (processed_characters_count) { + *processed_characters_count = 0; + } + if (throw_on_error) { + throw Exception(u"Read invalid character."); + } else { + return {false, 0}; + } + } + } + + if (allow_trailing_spaces) { + while (current != end && IsSpace(*current)) { + current++; + } + } + + if (current != end) { + throw Exception(u"There is trailing junk."); + } + + return {negate, result}; +} +} // namespace cru diff --git a/src/platform/graphics/Geometry.cpp b/src/platform/graphics/Geometry.cpp index 32ba0d6d..c215ad30 100644 --- a/src/platform/graphics/Geometry.cpp +++ b/src/platform/graphics/Geometry.cpp @@ -193,7 +193,7 @@ void IGeometryBuilder::ParseAndApplySvgPathData(StringView path_d) { Index processed_count = 0; auto result = path_d.substr(position).ParseToFloat( - &processed_count, StringToFloatFlags::kAllowTrailingJunk); + &processed_count, StringToNumberFlags::kAllowTrailingJunk); if (std::isnan(result)) throw Exception(u"Invalid svg path data number."); |