diff options
author | crupest <crupest@outlook.com> | 2022-03-10 16:43:04 +0800 |
---|---|---|
committer | crupest <crupest@outlook.com> | 2022-03-10 16:43:04 +0800 |
commit | 51f87e3ff980e62f9cb5ee656e5591412e2766eb (patch) | |
tree | 4101a0c00fd77dfc694c377ae2b69178a77cdb63 | |
parent | 0b5c16f6b35f7144b34996d8c77f370bcbcf150c (diff) | |
download | cru-51f87e3ff980e62f9cb5ee656e5591412e2766eb.tar.gz cru-51f87e3ff980e62f9cb5ee656e5591412e2766eb.tar.bz2 cru-51f87e3ff980e62f9cb5ee656e5591412e2766eb.zip |
...
-rw-r--r-- | include/cru/common/String.h | 34 | ||||
-rw-r--r-- | include/cru/common/StringToNumberConverter.h | 19 | ||||
-rw-r--r-- | src/common/String.cpp | 31 | ||||
-rw-r--r-- | src/common/StringToNumberConverter.cpp | 41 |
4 files changed, 106 insertions, 19 deletions
diff --git a/include/cru/common/String.h b/include/cru/common/String.h index 7bbbecfb..c1ea839c 100644 --- a/include/cru/common/String.h +++ b/include/cru/common/String.h @@ -11,6 +11,7 @@ #include <iterator> #include <string> #include <string_view> +#include <type_traits> #include <vector> namespace cru { @@ -212,6 +213,17 @@ class CRU_BASE_API String { Range RangeFromCodeUnitToCodePoint(Range code_unit_range) const; Range RangeFromCodePointToCodeUnit(Range code_point_range) const; + template <typename TInteger> + std::enable_if_t<std::is_signed_v<TInteger>, TInteger> ParseToInteger( + Index* processed_characters_count, unsigned flags, int base) const; + + int ParseToInt(Index* processed_characters_count = nullptr, + unsigned flags = StringToNumberFlags::kNoFlags, + int base = 0) const; + long long ParseToLongLong(Index* processed_characters_count = nullptr, + unsigned flags = StringToNumberFlags::kNoFlags, + int base = 0) const; + float ParseToFloat(Index* processed_characters_count = nullptr, unsigned flags = StringToNumberFlags::kNoFlags) const; double ParseToDouble(Index* processed_characters_count = nullptr, @@ -339,6 +351,21 @@ class CRU_BASE_API StringView { Range RangeFromCodeUnitToCodePoint(Range code_unit_range) const; Range RangeFromCodePointToCodeUnit(Range code_point_range) const; + template <typename TInteger> + std::enable_if_t<std::is_signed_v<TInteger>, TInteger> ParseToInteger( + Index* processed_characters_count, unsigned flags, int base) const { + auto result = StringToIntegerConverterImpl(flags, base) + .Parse(data(), size(), processed_characters_count); + return result.negate ? -result.value : result.value; + } + + int ParseToInt(Index* processed_characters_count = nullptr, + unsigned flags = StringToNumberFlags::kNoFlags, + int base = 0) const; + long long ParseToLongLong(Index* processed_characters_count = nullptr, + unsigned flags = StringToNumberFlags::kNoFlags, + int base = 0) const; + float ParseToFloat(Index* processed_characters_count = nullptr, unsigned flags = StringToNumberFlags::kNoFlags) const; double ParseToDouble(Index* processed_characters_count = nullptr, @@ -434,6 +461,13 @@ inline Index Utf16NextWord(StringView str, Index position, String CRU_BASE_API ToLower(StringView s); String CRU_BASE_API ToUpper(StringView s); + +template <typename TInteger> +std::enable_if_t<std::is_signed_v<TInteger>, TInteger> String::ParseToInteger( + Index* processed_characters_count, unsigned flags, int base) const { + View().ParseToInteger<TInteger>(processed_characters_count, flags, base); +} + } // namespace cru template <> diff --git a/include/cru/common/StringToNumberConverter.h b/include/cru/common/StringToNumberConverter.h index b5118de6..f1baafee 100644 --- a/include/cru/common/StringToNumberConverter.h +++ b/include/cru/common/StringToNumberConverter.h @@ -4,7 +4,7 @@ #include <ostream> namespace cru { -struct StringToNumberFlags { +struct CRU_BASE_API StringToNumberFlags { constexpr static unsigned kNoFlags = 0; constexpr static unsigned kAllowLeadingSpaces = 1 << 0; constexpr static unsigned kAllowTrailingSpaces = 1 << 1; @@ -13,7 +13,7 @@ struct StringToNumberFlags { constexpr static unsigned kThrowOnError = 1 << 3; }; -struct StringToIntegerConverterImplResult { +struct CRU_BASE_API StringToIntegerConverterImplResult { StringToIntegerConverterImplResult() = default; StringToIntegerConverterImplResult(bool negate, unsigned long long value) : negate(negate), value(value) {} @@ -22,13 +22,15 @@ struct StringToIntegerConverterImplResult { unsigned long long value; }; -inline bool operator==(const StringToIntegerConverterImplResult& left, - const StringToIntegerConverterImplResult& right) { +inline bool CRU_BASE_API +operator==(const StringToIntegerConverterImplResult& left, + const StringToIntegerConverterImplResult& right) { return left.negate == right.negate && left.value == right.value; } -inline bool operator!=(const StringToIntegerConverterImplResult& left, - const StringToIntegerConverterImplResult& right) { +inline bool CRU_BASE_API +operator!=(const StringToIntegerConverterImplResult& left, + const StringToIntegerConverterImplResult& right) { return !(left == right); } @@ -41,7 +43,7 @@ inline std::ostream& operator<<( /** * \brief A converter that convert number into long long. */ -struct StringToIntegerConverterImpl { +struct CRU_BASE_API StringToIntegerConverterImpl { public: explicit StringToIntegerConverterImpl(unsigned flags, int base = 0) : flags(flags), base(base) {} @@ -58,6 +60,9 @@ struct StringToIntegerConverterImpl { StringToIntegerConverterImplResult Parse( const char* str, Index size, Index* processed_characters_count) const; + StringToIntegerConverterImplResult Parse( + const char16_t* str, Index size, Index* processed_characters_count) const; + template <std::size_t Size> StringToIntegerConverterImplResult Parse( const char (&str)[Size], Index* processed_characters_count) const { diff --git a/src/common/String.cpp b/src/common/String.cpp index 908f64b6..bb31b987 100644 --- a/src/common/String.cpp +++ b/src/common/String.cpp @@ -2,6 +2,7 @@ #include <double-conversion/double-conversion.h> #include <double-conversion/string-to-double.h> #include "cru/common/Exception.h" +#include "cru/common/StringToNumberConverter.h" #include "cru/common/StringUtil.h" #include <cmath> @@ -313,6 +314,16 @@ Range String::RangeFromCodePointToCodeUnit(Range code_point_range) const { return View().RangeFromCodePointToCodeUnit(code_point_range); } +int String::ParseToInt(Index* processed_characters_count, unsigned flags, + int base) const { + return View().ParseToInt(processed_characters_count, flags, base); +} + +long long String::ParseToLongLong(Index* processed_characters_count, + unsigned flags, int base) const { + return View().ParseToLongLong(processed_characters_count, flags, base); +} + float String::ParseToFloat(Index* processed_characters_count, unsigned flags) const { return View().ParseToFloat(processed_characters_count, flags); @@ -517,6 +528,16 @@ std::string StringView::ToUtf8() const { return result; } +int StringView::ParseToInt(Index* processed_characters_count, unsigned flags, + int base) const { + return ParseToInteger<int>(processed_characters_count, flags, base); +} + +long long StringView::ParseToLongLong(Index* processed_characters_count, + unsigned flags, int base) const { + return ParseToInteger<long long>(processed_characters_count, flags, base); +} + static int MapStringToDoubleFlags(int flags) { int f = double_conversion::StringToDoubleConverter::ALLOW_CASE_INSENSIBILITY; if (flags & StringToNumberFlags::kAllowLeadingSpaces) { @@ -544,6 +565,11 @@ float StringView::ParseToFloat(Index* processed_characters_count, if (processed_characters_count != nullptr) { *processed_characters_count = pcc; } + + if (flags & StringToNumberFlags::kThrowOnError && std::isnan(result)) { + throw Exception(u"Result of string to float conversion is NaN"); + } + return result; } @@ -555,6 +581,11 @@ double StringView::ParseToDouble(Index* processed_characters_count, if (processed_characters_count != nullptr) { *processed_characters_count = pcc; } + + if (flags & StringToNumberFlags::kThrowOnError && std::isnan(result)) { + throw Exception(u"Result of string to double conversion is NaN"); + } + return result; } diff --git a/src/common/StringToNumberConverter.cpp b/src/common/StringToNumberConverter.cpp index 526c2712..e20e436c 100644 --- a/src/common/StringToNumberConverter.cpp +++ b/src/common/StringToNumberConverter.cpp @@ -10,20 +10,24 @@ 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 { +namespace { +template <typename T> +StringToIntegerConverterImplResult GenericParseInteger( + const StringToIntegerConverterImpl* converter, const T* const str, + const Index size, Index* processed_characters_count) { 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."); + if (!converter->CheckParams()) + throw std::invalid_argument("Invalid parsing flags."); - const bool throw_on_error = (flags & StringToNumberFlags::kThrowOnError) != 0; + const bool throw_on_error = + (converter->flags & StringToNumberFlags::kThrowOnError) != 0; - const char* const end = str + size; + auto const end = str + size; - const char* current = str; + auto current = str; - if (flags & StringToNumberFlags::kAllowLeadingSpaces) { + if (converter->flags & StringToNumberFlags::kAllowLeadingSpaces) { while (current != end && IsSpace(*current)) { current++; } @@ -60,7 +64,7 @@ StringToIntegerConverterImplResult StringToIntegerConverterImpl::Parse( } } - int base = this->base; + int base = converter->base; if (base == 0) { if (*current == '0') { @@ -96,7 +100,7 @@ StringToIntegerConverterImplResult StringToIntegerConverterImpl::Parse( } const bool allow_leading_zero = - flags & StringToNumberFlags::kAllowLeadingZeroForInteger; + converter->flags & StringToNumberFlags::kAllowLeadingZeroForInteger; while (current != end && *current == '0') { current++; @@ -110,9 +114,9 @@ StringToIntegerConverterImplResult StringToIntegerConverterImpl::Parse( } const bool allow_trailing_junk = - flags & StringToNumberFlags::kAllowTrailingJunk; + converter->flags & StringToNumberFlags::kAllowTrailingJunk; const bool allow_trailing_spaces = - flags & StringToNumberFlags::kAllowTrailingSpaces; + converter->flags & StringToNumberFlags::kAllowTrailingSpaces; unsigned long long result = 0; while (current != end) { @@ -165,4 +169,17 @@ StringToIntegerConverterImplResult StringToIntegerConverterImpl::Parse( return {negate, result}; } +} // namespace + +StringToIntegerConverterImplResult StringToIntegerConverterImpl::Parse( + const char* const str, const Index size, + Index* processed_characters_count) const { + return GenericParseInteger(this, str, size, processed_characters_count); +} + +StringToIntegerConverterImplResult StringToIntegerConverterImpl::Parse( + const char16_t* const str, const Index size, + Index* processed_characters_count) const { + return GenericParseInteger(this, str, size, processed_characters_count); +} } // namespace cru |