diff options
author | crupest <crupest@outlook.com> | 2022-03-09 23:12:26 +0800 |
---|---|---|
committer | crupest <crupest@outlook.com> | 2022-03-09 23:12:26 +0800 |
commit | dceef749139061fdac4946df77219f1cc8aa6483 (patch) | |
tree | 0b413e605183226034fdaf342adfd6dc5e8fc08e /src/common/StringToNumberConverter.cpp | |
parent | 78f5221e8fbab510bb8b5ac268b7d42bed762961 (diff) | |
download | cru-dceef749139061fdac4946df77219f1cc8aa6483.tar.gz cru-dceef749139061fdac4946df77219f1cc8aa6483.tar.bz2 cru-dceef749139061fdac4946df77219f1cc8aa6483.zip |
...
Diffstat (limited to 'src/common/StringToNumberConverter.cpp')
-rw-r--r-- | src/common/StringToNumberConverter.cpp | 154 |
1 files changed, 154 insertions, 0 deletions
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 |