aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorcrupest <crupest@outlook.com>2022-03-10 16:01:56 +0800
committercrupest <crupest@outlook.com>2022-03-10 16:01:56 +0800
commit0b5c16f6b35f7144b34996d8c77f370bcbcf150c (patch)
treeda6039de16d0de9aa8c6ee1afdcec44133ce4133
parent268bec4cd0d562394c2c27d10a26be1264bc8648 (diff)
downloadcru-0b5c16f6b35f7144b34996d8c77f370bcbcf150c.tar.gz
cru-0b5c16f6b35f7144b34996d8c77f370bcbcf150c.tar.bz2
cru-0b5c16f6b35f7144b34996d8c77f370bcbcf150c.zip
...
-rw-r--r--include/cru/common/String.h17
-rw-r--r--include/cru/common/StringToNumberConverter.h28
-rw-r--r--src/common/StringToNumberConverter.cpp27
-rw-r--r--test/common/CMakeLists.txt1
-rw-r--r--test/common/StringToNumberConverterTest.cpp159
5 files changed, 218 insertions, 14 deletions
diff --git a/include/cru/common/String.h b/include/cru/common/String.h
index 9ec494c6..7bbbecfb 100644
--- a/include/cru/common/String.h
+++ b/include/cru/common/String.h
@@ -356,7 +356,22 @@ class CRU_BASE_API StringView {
CRU_DEFINE_COMPARE_OPERATORS(String)
inline String operator+(const String& left, const String& right) {
- String result(left);
+ String result;
+ result += left;
+ result += right;
+ return result;
+}
+
+inline String operator+(String::value_type left, const String& right) {
+ String result;
+ result += left;
+ result += right;
+ return result;
+}
+
+inline String operator+(const String& left, String::value_type right) {
+ String result;
+ result += left;
result += right;
return result;
}
diff --git a/include/cru/common/StringToNumberConverter.h b/include/cru/common/StringToNumberConverter.h
index e68d12a6..b5118de6 100644
--- a/include/cru/common/StringToNumberConverter.h
+++ b/include/cru/common/StringToNumberConverter.h
@@ -1,6 +1,8 @@
#pragma once
#include "Base.h"
+#include <ostream>
+
namespace cru {
struct StringToNumberFlags {
constexpr static unsigned kNoFlags = 0;
@@ -12,10 +14,30 @@ struct StringToNumberFlags {
};
struct StringToIntegerConverterImplResult {
+ StringToIntegerConverterImplResult() = default;
+ StringToIntegerConverterImplResult(bool negate, unsigned long long value)
+ : negate(negate), value(value) {}
+
bool negate;
unsigned long long value;
};
+inline bool 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) {
+ return !(left == right);
+}
+
+inline std::ostream& operator<<(
+ std::ostream& stream, const StringToIntegerConverterImplResult& result) {
+ return stream << "StringToIntegerConverterImplResult("
+ << (result.negate ? "-" : "") << result.value << ")";
+}
+
/**
* \brief A converter that convert number into long long.
*/
@@ -36,6 +58,12 @@ struct StringToIntegerConverterImpl {
StringToIntegerConverterImplResult Parse(
const char* str, Index size, Index* processed_characters_count) const;
+ template <std::size_t Size>
+ StringToIntegerConverterImplResult Parse(
+ const char (&str)[Size], Index* processed_characters_count) const {
+ return Parse(str, Size - 1, processed_characters_count);
+ }
+
unsigned flags;
/**
* \brief The base of the number used for parse or 0 for auto detect.
diff --git a/src/common/StringToNumberConverter.cpp b/src/common/StringToNumberConverter.cpp
index d77f7886..526c2712 100644
--- a/src/common/StringToNumberConverter.cpp
+++ b/src/common/StringToNumberConverter.cpp
@@ -3,15 +3,7 @@
namespace cru {
bool StringToIntegerConverterImpl::CheckParams() const {
- if (base == 0) {
- if (flags & StringToNumberFlags::kAllowLeadingZeroForInteger) {
- return false;
- } else {
- return true;
- }
- }
-
- return base >= 2 & base <= 36;
+ return base == 0 || base >= 2 & base <= 36;
}
static bool IsSpace(char c) {
@@ -74,6 +66,9 @@ StringToIntegerConverterImplResult StringToIntegerConverterImpl::Parse(
if (*current == '0') {
++current;
if (current == end) {
+ if (processed_characters_count) {
+ *processed_characters_count = current - str;
+ }
return {negate, 0};
} else if (*current == 'x' || *current == 'X') {
++current;
@@ -84,6 +79,8 @@ StringToIntegerConverterImplResult StringToIntegerConverterImpl::Parse(
} else {
base = 8;
}
+ } else {
+ base = 10;
}
}
@@ -106,6 +103,9 @@ StringToIntegerConverterImplResult StringToIntegerConverterImpl::Parse(
}
if (current == end) {
+ if (processed_characters_count) {
+ *processed_characters_count = current - str;
+ }
return {negate, 0};
}
@@ -119,10 +119,13 @@ StringToIntegerConverterImplResult StringToIntegerConverterImpl::Parse(
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') {
+ current++;
+ } else if (base > 10 && c >= 'a' && c <= (base + 'a' - 10 - 1)) {
result = result * base + c - 'a' + 10;
- } else if (base > 10 && c >= 'A' && c <= 'Z') {
+ current++;
+ } else if (base > 10 && c >= 'A' && c <= (base + 'A' - 10 - 1)) {
result = result * base + c - 'A' + 10;
+ current++;
} else if (allow_trailing_junk) {
break;
} else if (allow_trailing_spaces && IsSpace(c)) {
@@ -132,7 +135,7 @@ StringToIntegerConverterImplResult StringToIntegerConverterImpl::Parse(
*processed_characters_count = 0;
}
if (throw_on_error) {
- throw Exception(u"Read invalid character.");
+ throw Exception(String(u"Read invalid character '") + c + u"'.");
} else {
return {false, 0};
}
diff --git a/test/common/CMakeLists.txt b/test/common/CMakeLists.txt
index 600c18c3..6b2b594b 100644
--- a/test/common/CMakeLists.txt
+++ b/test/common/CMakeLists.txt
@@ -2,6 +2,7 @@ add_executable(cru_base_test
HandlerRegistryTest.cpp
PropertyTreeTest.cpp
StringTest.cpp
+ StringToNumberConverterTest.cpp
StringUtilTest.cpp
)
target_link_libraries(cru_base_test PRIVATE cru_base cru_test_base)
diff --git a/test/common/StringToNumberConverterTest.cpp b/test/common/StringToNumberConverterTest.cpp
index 5eb4e33b..7b009d1e 100644
--- a/test/common/StringToNumberConverterTest.cpp
+++ b/test/common/StringToNumberConverterTest.cpp
@@ -1,7 +1,164 @@
+#include "cru/common/Exception.h"
#include "cru/common/StringToNumberConverter.h"
#include <gtest/gtest.h>
TEST(StringToIntegerConverterImpl, Base0) {
-
+ using namespace cru;
+ StringToIntegerConverterImpl converter(0, 0);
+ Index processed_characters_count;
+
+ ASSERT_EQ(converter.Parse("12345678", &processed_characters_count),
+ StringToIntegerConverterImplResult(false, 12345678));
+ ASSERT_EQ(processed_characters_count, 8);
+
+ ASSERT_EQ(converter.Parse("0", &processed_characters_count),
+ StringToIntegerConverterImplResult(false, 0));
+ ASSERT_EQ(processed_characters_count, 1);
+
+ ASSERT_EQ(converter.Parse("012", &processed_characters_count),
+ StringToIntegerConverterImplResult(false, 012));
+ ASSERT_EQ(processed_characters_count, 3);
+
+ ASSERT_EQ(converter.Parse("0x12", &processed_characters_count),
+ StringToIntegerConverterImplResult(false, 0x12));
+ ASSERT_EQ(processed_characters_count, 4);
+
+ ASSERT_EQ(converter.Parse("0X12", &processed_characters_count),
+ StringToIntegerConverterImplResult(false, 0x12));
+ ASSERT_EQ(processed_characters_count, 4);
+
+ ASSERT_EQ(converter.Parse("0b101", &processed_characters_count),
+ StringToIntegerConverterImplResult(false, 0b101));
+ ASSERT_EQ(processed_characters_count, 5);
+
+ ASSERT_EQ(converter.Parse("0B101", &processed_characters_count),
+ StringToIntegerConverterImplResult(false, 0b101));
+ ASSERT_EQ(processed_characters_count, 5);
+
+ ASSERT_EQ(converter.Parse("-123", &processed_characters_count),
+ StringToIntegerConverterImplResult(true, 123));
+ ASSERT_EQ(processed_characters_count, 4);
+
+ ASSERT_EQ(converter.Parse("-0x10", &processed_characters_count),
+ StringToIntegerConverterImplResult(true, 0x10));
+ ASSERT_EQ(processed_characters_count, 5);
+}
+
+TEST(StringToIntegerConverterImpl, Base0ForError) {
+ using namespace cru;
+ StringToIntegerConverterImpl converter(0, 0);
+ Index processed_characters_count;
+
+ ASSERT_EQ(converter.Parse("a", &processed_characters_count),
+ StringToIntegerConverterImplResult(false, 0));
+ ASSERT_EQ(processed_characters_count, 0);
+
+ ASSERT_EQ(converter.Parse("0a", &processed_characters_count),
+ StringToIntegerConverterImplResult(false, 0));
+ ASSERT_EQ(processed_characters_count, 0);
+
+ ASSERT_EQ(converter.Parse("0x", &processed_characters_count),
+ StringToIntegerConverterImplResult(false, 0));
+ ASSERT_EQ(processed_characters_count, 0);
+
+ ASSERT_EQ(converter.Parse("0xx", &processed_characters_count),
+ StringToIntegerConverterImplResult(false, 0));
+ ASSERT_EQ(processed_characters_count, 0);
+
+ ASSERT_EQ(converter.Parse(" 0", &processed_characters_count),
+ StringToIntegerConverterImplResult(false, 0));
+ ASSERT_EQ(processed_characters_count, 0);
+
+ ASSERT_EQ(converter.Parse("0 ", &processed_characters_count),
+ StringToIntegerConverterImplResult(false, 0));
+ ASSERT_EQ(processed_characters_count, 0);
+}
+
+TEST(StringToIntegerConverterImpl, ThrowOnErrorFlag) {
+ using namespace cru;
+ StringToIntegerConverterImpl converter(StringToNumberFlags::kThrowOnError, 0);
+ Index processed_characters_count;
+ ASSERT_THROW(converter.Parse("?", &processed_characters_count), Exception);
+}
+
+TEST(StringToIntegerConverterImpl, AllowLeadingZeroFlag) {
+ using namespace cru;
+ StringToIntegerConverterImpl converter(
+ StringToNumberFlags::kAllowLeadingSpaces, 0);
+ Index processed_characters_count;
+ ASSERT_EQ(converter.Parse(" 123", &processed_characters_count),
+ StringToIntegerConverterImplResult(false, 123));
+ ASSERT_EQ(processed_characters_count, 6);
+}
+
+TEST(StringToIntegerConverterImpl, AllowTrailingZeroFlag) {
+ using namespace cru;
+ StringToIntegerConverterImpl converter(
+ StringToNumberFlags::kAllowTrailingSpaces, 0);
+ Index processed_characters_count;
+ ASSERT_EQ(converter.Parse("123 ", &processed_characters_count),
+ StringToIntegerConverterImplResult(false, 123));
+ ASSERT_EQ(processed_characters_count, 6);
+}
+
+TEST(StringToIntegerConverterImpl, AllowTrailingJunk) {
+ using namespace cru;
+ StringToIntegerConverterImpl converter(
+ StringToNumberFlags::kAllowLeadingZeroForInteger, 0);
+ Index processed_characters_count;
+ ASSERT_EQ(converter.Parse("123 12", &processed_characters_count),
+ StringToIntegerConverterImplResult(false, 123));
+ ASSERT_EQ(processed_characters_count, 3);
+}
+
+TEST(StringToIntegerConverterImpl, AllowLeadingZeroForInteger) {
+ using namespace cru;
+ StringToIntegerConverterImpl converter(
+ StringToNumberFlags::kAllowLeadingZeroForInteger, 0);
+ Index processed_characters_count;
+ ASSERT_EQ(converter.Parse("0x0012", &processed_characters_count),
+ StringToIntegerConverterImplResult(false, 0x12));
+ ASSERT_EQ(processed_characters_count, 6);
+
+ ASSERT_EQ(converter.Parse("000011", &processed_characters_count),
+ StringToIntegerConverterImplResult(false, 000011));
+ ASSERT_EQ(processed_characters_count, 6);
+
+ ASSERT_EQ(converter.Parse("0b0011", &processed_characters_count),
+ StringToIntegerConverterImplResult(false, 0b0011));
+ ASSERT_EQ(processed_characters_count, 6);
+}
+
+TEST(StringToIntegerConverterImpl, CompositeFlags) {
+ using namespace cru;
+ StringToIntegerConverterImpl converter(
+ StringToNumberFlags::kAllowLeadingSpaces |
+ StringToNumberFlags::kAllowTrailingJunk |
+ StringToNumberFlags::kAllowLeadingZeroForInteger,
+ 0);
+ Index processed_characters_count;
+
+ ASSERT_EQ(converter.Parse(" 0x00123!!!", &processed_characters_count),
+ StringToIntegerConverterImplResult(false, 0x00123));
+ ASSERT_EQ(processed_characters_count, 10);
+}
+
+TEST(StringToIntegerConverterImpl, OtherBase) {
+ using namespace cru;
+ StringToIntegerConverterImpl converter(0, 7);
+ Index processed_characters_count;
+
+ ASSERT_EQ(converter.Parse("12", &processed_characters_count),
+ StringToIntegerConverterImplResult(false, 9));
+ ASSERT_EQ(processed_characters_count, 2);
+
+ ASSERT_EQ(converter.Parse("-12", &processed_characters_count),
+ StringToIntegerConverterImplResult(true, 9));
+ ASSERT_EQ(processed_characters_count, 3);
+
+ converter.base = 11;
+ ASSERT_EQ(converter.Parse("1a", &processed_characters_count),
+ StringToIntegerConverterImplResult(false, 21));
+ ASSERT_EQ(processed_characters_count, 2);
}