aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitmodules3
-rw-r--r--CMakeLists.txt1
-rw-r--r--include/cru/base/String.h47
-rw-r--r--include/cru/base/StringToNumberConverter.h106
-rw-r--r--include/cru/base/StringUtil.h88
m---------lib/double-conversion0
-rw-r--r--src/base/CMakeLists.txt3
-rw-r--r--src/base/String.cpp121
-rw-r--r--src/base/StringToNumberConverter.cpp170
-rw-r--r--src/platform/graphics/Geometry.cpp14
-rw-r--r--src/ui/components/Input.cpp23
-rw-r--r--src/ui/mapper/ColorMapper.cpp6
-rw-r--r--src/ui/mapper/FontMapper.cpp4
-rw-r--r--src/ui/mapper/MeasureLengthMapper.cpp2
-rw-r--r--src/ui/mapper/PointMapper.cpp5
-rw-r--r--src/ui/mapper/SizeMapper.cpp5
-rw-r--r--src/ui/mapper/ThicknessMapper.cpp4
-rw-r--r--test/base/CMakeLists.txt1
-rw-r--r--test/base/StringTest.cpp26
-rw-r--r--test/base/StringToNumberConverterTest.cpp166
-rw-r--r--test/base/StringUtilTest.cpp112
21 files changed, 231 insertions, 676 deletions
diff --git a/.gitmodules b/.gitmodules
index c87b5454..d49fb062 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,6 +1,3 @@
-[submodule "lib/double-conversion"]
- path = lib/double-conversion
- url = https://github.com/google/double-conversion.git
[submodule "lib/Catch2"]
path = lib/Catch2
url = https://github.com/catchorg/Catch2.git
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 6da50c5b..980ff1ff 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -16,7 +16,6 @@ if (APPLE)
endif()
add_subdirectory(lib/Catch2)
-add_subdirectory(lib/double-conversion)
enable_testing()
diff --git a/include/cru/base/String.h b/include/cru/base/String.h
index 7d462a6e..313a3ce2 100644
--- a/include/cru/base/String.h
+++ b/include/cru/base/String.h
@@ -3,7 +3,6 @@
#include "Buffer.h"
#include "Range.h"
-#include "StringToNumberConverter.h"
#include "StringUtil.h"
#include <filesystem>
@@ -11,7 +10,6 @@
#include <iterator>
#include <string>
#include <string_view>
-#include <type_traits>
#include <vector>
namespace cru {
@@ -215,22 +213,6 @@ 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,
- StringToNumberFlag flags = {}, int base = 0) const;
- long long ParseToLongLong(Index* processed_characters_count = nullptr,
- StringToNumberFlag flags = {}, int base = 0) const;
-
- float ParseToFloat(Index* processed_characters_count = nullptr,
- StringToNumberFlag flags = {}) const;
- double ParseToDouble(Index* processed_characters_count = nullptr,
- StringToNumberFlag flags = {}) const;
- std::vector<float> ParseToFloatList(value_type separator = u' ') const;
- std::vector<double> ParseToDoubleList(value_type separator = u' ') const;
-
#ifdef CRU_PLATFORM_WINDOWS
const wchar_t* WinCStr() const {
return reinterpret_cast<const wchar_t*>(c_str());
@@ -351,29 +333,6 @@ 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, StringToNumberFlag flags,
- int base) const {
- auto utf8_string = ToUtf8();
- auto result = StringToIntegerConverter(flags, base)
- .Parse(utf8_string.data(), utf8_string.size(),
- processed_characters_count);
- return result.negate ? -result.value : result.value;
- }
-
- int ParseToInt(Index* processed_characters_count = nullptr,
- StringToNumberFlag flags = {}, int base = 0) const;
- long long ParseToLongLong(Index* processed_characters_count = nullptr,
- StringToNumberFlag flags = {}, int base = 0) const;
-
- float ParseToFloat(Index* processed_characters_count = nullptr,
- StringToNumberFlag flags = {}) const;
- double ParseToDouble(Index* processed_characters_count = nullptr,
- StringToNumberFlag flags = {}) const;
- std::vector<float> ParseToFloatList(value_type separator = u' ') const;
- std::vector<double> ParseToDoubleList(value_type separator = u' ') const;
-
std::string ToUtf8() const;
Buffer ToUtf8Buffer(bool end_zero = true) const;
@@ -464,12 +423,6 @@ 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/base/StringToNumberConverter.h b/include/cru/base/StringToNumberConverter.h
deleted file mode 100644
index 64c29971..00000000
--- a/include/cru/base/StringToNumberConverter.h
+++ /dev/null
@@ -1,106 +0,0 @@
-#pragma once
-#include "Base.h"
-#include "Bitmask.h"
-
-#include <cstddef>
-#include <ostream>
-
-namespace cru {
-namespace details {
-struct StringToNumberFlagTag {};
-} // namespace details
-
-using StringToNumberFlag = Bitmask<details::StringToNumberFlagTag>;
-
-struct StringToNumberFlags {
- constexpr static StringToNumberFlag kAllowLeadingSpaces =
- StringToNumberFlag::FromOffset(1);
- constexpr static StringToNumberFlag kAllowTrailingSpaces =
- StringToNumberFlag::FromOffset(2);
- constexpr static StringToNumberFlag kAllowTrailingJunk =
- StringToNumberFlag::FromOffset(3);
- constexpr static StringToNumberFlag kAllowLeadingZeroForInteger =
- StringToNumberFlag::FromOffset(4);
- constexpr static StringToNumberFlag kThrowOnError =
- StringToNumberFlag::FromOffset(5);
-};
-
-template <typename TResult>
-struct IStringToNumberConverter : virtual Interface {
- virtual TResult Parse(const char* str, Index size,
- Index* processed_characters_count) const = 0;
-
- template <std::size_t Size>
- TResult Parse(const char (&str)[Size],
- Index* processed_characters_count) const {
- return Parse(str, Size - 1, processed_characters_count);
- }
-};
-
-struct CRU_BASE_API StringToIntegerResult {
- StringToIntegerResult() = default;
- StringToIntegerResult(bool negate, unsigned long long value)
- : negate(negate), value(value) {}
-
- bool negate;
- unsigned long long value;
-};
-
-inline bool CRU_BASE_API operator==(const StringToIntegerResult& left,
- const StringToIntegerResult& right) {
- return left.negate == right.negate && left.value == right.value;
-}
-
-inline bool CRU_BASE_API operator!=(const StringToIntegerResult& left,
- const StringToIntegerResult& right) {
- return !(left == right);
-}
-
-CRU_BASE_API inline std::ostream& operator<<(std::ostream& stream, const StringToIntegerResult& result) {
- return stream << "StringToIntegerConverterImplResult("
- << (result.negate ? "-" : "") << result.value << ")";
-}
-
-/**
- * \brief A converter that convert number into long long.
- */
-struct CRU_BASE_API StringToIntegerConverter
- : IStringToNumberConverter<StringToIntegerResult> {
- public:
- explicit StringToIntegerConverter(StringToNumberFlag flags, int base = 0)
- : flags(flags), base(base) {}
-
- bool CheckParams() const;
-
- /**
- * \brief Convert string to long long.
- * \param str The string to convert.
- * \param size The size of the string.
- * \param processed_characters_count The number of characters that were
- * processed. Or nullptr to not retrieve.
- */
- StringToIntegerResult Parse(const char* str, Index size,
- Index* processed_characters_count) const override;
- using IStringToNumberConverter<StringToIntegerResult>::Parse;
-
- StringToNumberFlag flags;
- /**
- * \brief The base of the number used for parse or 0 for auto detect.
- * \remarks Base can only be of range [2, 36] or 0. If base is 0, decimal is
- * assumed by default ,or if str is started with "0x" or "0X" hexadecimal is
- * assumed, or if str is started with a single "0" octoral is assumed, or if
- * str is started with "0b" or "0B" binary is assumed. Otherwise it is an
- * error.
- */
- int base;
-};
-
-struct CRU_BASE_API StringToFloatConverter {
- StringToFloatConverter(StringToNumberFlag flags) : flags(flags) {}
-
- double Parse(const char* str, Index size,
- Index* processed_characters_count) const;
-
- StringToNumberFlag flags;
-};
-} // namespace cru
diff --git a/include/cru/base/StringUtil.h b/include/cru/base/StringUtil.h
index 2b1b5914..258d884e 100644
--- a/include/cru/base/StringUtil.h
+++ b/include/cru/base/StringUtil.h
@@ -2,11 +2,15 @@
#include "Base.h"
#include "Bitmask.h"
+#include <algorithm>
+#include <cctype>
+#include <charconv>
#include <compare>
#include <format>
#include <functional>
#include <string>
#include <string_view>
+#include <system_error>
#include <type_traits>
#include <vector>
@@ -31,6 +35,90 @@ struct SplitOptions {
std::vector<std::string> Split(std::string_view str, std::string_view sep,
SplitOption options = {});
+namespace details {
+struct ParseToNumberFlagTag {};
+} // namespace details
+
+using ParseToNumberFlag = Bitmask<details::ParseToNumberFlagTag>;
+
+struct ParseToNumberFlags {
+ constexpr static ParseToNumberFlag AllowLeadingSpaces =
+ ParseToNumberFlag::FromOffset(1);
+ constexpr static ParseToNumberFlag AllowTrailingSpaces =
+ ParseToNumberFlag::FromOffset(2);
+ constexpr static ParseToNumberFlag AllowTrailingJunk =
+ ParseToNumberFlag::FromOffset(3);
+};
+
+template <typename T>
+struct ParseToNumberResult {
+ bool valid;
+ T value;
+ Index processed_char_count;
+ std::string message;
+};
+
+template <typename T>
+ParseToNumberResult<T> ParseToNumber(std::string_view str,
+ ParseToNumberFlag flags = {}) {
+ ParseToNumberResult<T> result{};
+
+ const char* ptr = str.data();
+ const char* const begin = str.data();
+ const char* const end = str.data() + str.size();
+ if (flags.Has(ParseToNumberFlags::AllowLeadingSpaces)) {
+ while (ptr != str.data() + str.size() && isspace(*ptr)) {
+ ptr++;
+ }
+ }
+
+ if (ptr == end) {
+ result.valid = false;
+ result.message =
+ "Parsing reached the end (after reading all leading spaces).";
+ return result;
+ }
+
+ auto parse_result =
+ std::from_chars(ptr, str.data() + str.size(), result.value);
+ if (parse_result.ec == std::errc::invalid_argument) {
+ result.valid = false;
+ result.message = "Not a valid number.";
+ return result;
+ } else if (parse_result.ec == std::errc::result_out_of_range) {
+ result.valid = false;
+ result.message = "Value out of range.";
+ return result;
+ } else {
+ if (parse_result.ptr == end ||
+ flags.Has(ParseToNumberFlags::AllowTrailingJunk) ||
+ (flags.Has(ParseToNumberFlags::AllowTrailingSpaces) &&
+ IsSpace(std::string_view(parse_result.ptr, end)))) {
+ result.valid = true;
+ result.processed_char_count = parse_result.ptr - str.data();
+ return result;
+ } else {
+ result.valid = false;
+ result.message = "There are junk trailing characters.";
+ return result;
+ }
+ }
+}
+
+template <typename T>
+std::vector<T> ParseToNumberList(std::string_view str,
+ std::string_view separator = " ") {
+ auto segs = Split(str, separator, SplitOptions::RemoveSpace);
+ std::vector<T> result;
+ for (const auto& seg : segs) {
+ result.push_back(
+ ParseToNumber<T>(Trim(seg), ParseToNumberFlags::AllowLeadingSpaces |
+ ParseToNumberFlags::AllowTrailingSpaces)
+ .value);
+ }
+ return result;
+}
+
template <typename T>
struct ImplementFormatterByToString {
template <class ParseContext>
diff --git a/lib/double-conversion b/lib/double-conversion
deleted file mode 160000
-Subproject 7630f84a10f9428b041d0471e71a562141e9684
diff --git a/src/base/CMakeLists.txt b/src/base/CMakeLists.txt
index 7e7b0127..685ceb8c 100644
--- a/src/base/CMakeLists.txt
+++ b/src/base/CMakeLists.txt
@@ -5,7 +5,6 @@ add_library(CruBase
Format.cpp
PropertyTree.cpp
String.cpp
- StringToNumberConverter.cpp
StringUtil.cpp
SubProcess.cpp
io/AutoReadStream.cpp
@@ -85,5 +84,3 @@ else()
CRU_PLATFORM_UNIX
)
endif()
-
-target_link_libraries(CruBase PUBLIC double-conversion)
diff --git a/src/base/String.cpp b/src/base/String.cpp
index c90b9a71..c96b898d 100644
--- a/src/base/String.cpp
+++ b/src/base/String.cpp
@@ -2,14 +2,9 @@
#include "cru/base/Buffer.h"
#include "cru/base/Exception.h"
-#include "cru/base/StringToNumberConverter.h"
#include "cru/base/StringUtil.h"
-#include <double-conversion/double-conversion.h>
-#include <double-conversion/string-to-double.h>
-
#include <algorithm>
-#include <cmath>
#include <cstring>
#include <functional>
@@ -344,34 +339,6 @@ Range String::RangeFromCodePointToCodeUnit(Range code_point_range) const {
return View().RangeFromCodePointToCodeUnit(code_point_range);
}
-int String::ParseToInt(Index* processed_characters_count,
- StringToNumberFlag flags, int base) const {
- return View().ParseToInt(processed_characters_count, flags, base);
-}
-
-long long String::ParseToLongLong(Index* processed_characters_count,
- StringToNumberFlag flags, int base) const {
- return View().ParseToLongLong(processed_characters_count, flags, base);
-}
-
-float String::ParseToFloat(Index* processed_characters_count,
- StringToNumberFlag flags) const {
- return View().ParseToFloat(processed_characters_count, flags);
-}
-
-double String::ParseToDouble(Index* processed_characters_count,
- StringToNumberFlag flags) const {
- return View().ParseToDouble(processed_characters_count, flags);
-}
-
-std::vector<float> String::ParseToFloatList(value_type separator) const {
- return View().ParseToFloatList(separator);
-}
-
-std::vector<double> String::ParseToDoubleList(value_type separator) const {
- return View().ParseToDoubleList(separator);
-}
-
std::ostream& operator<<(std::ostream& os, const String& value) {
os << value.ToUtf8();
return os;
@@ -583,94 +550,6 @@ Buffer StringView::ToUtf8Buffer(bool end_zero) const {
return buffer;
}
-int StringView::ParseToInt(Index* processed_characters_count,
- StringToNumberFlag flags, int base) const {
- return ParseToInteger<int>(processed_characters_count, flags, base);
-}
-
-long long StringView::ParseToLongLong(Index* processed_characters_count,
- StringToNumberFlag flags,
- int base) const {
- return ParseToInteger<long long>(processed_characters_count, flags, base);
-}
-
-static int MapStringToDoubleFlags(StringToNumberFlag flags) {
- int f = double_conversion::StringToDoubleConverter::ALLOW_CASE_INSENSIBILITY;
- if (flags & StringToNumberFlags::kAllowLeadingSpaces) {
- f |= double_conversion::StringToDoubleConverter::ALLOW_LEADING_SPACES;
- }
- if (flags & StringToNumberFlags::kAllowTrailingSpaces) {
- f |= double_conversion::StringToDoubleConverter::ALLOW_TRAILING_SPACES;
- }
- if (flags & StringToNumberFlags::kAllowTrailingJunk) {
- f |= double_conversion::StringToDoubleConverter::ALLOW_TRAILING_JUNK;
- }
- return f;
-}
-
-static double_conversion::StringToDoubleConverter CreateStringToDoubleConverter(
- StringToNumberFlag flags) {
- return {MapStringToDoubleFlags(flags), 0.0, NAN, "inf", "nan"};
-}
-
-float StringView::ParseToFloat(Index* processed_characters_count,
- StringToNumberFlag flags) const {
- int pcc;
- auto result = CreateStringToDoubleConverter(flags).StringToFloat(
- reinterpret_cast<const uc16*>(ptr_), static_cast<int>(size_), &pcc);
- if (processed_characters_count != nullptr) {
- *processed_characters_count = pcc;
- }
-
- if (flags & StringToNumberFlags::kThrowOnError && std::isnan(result)) {
- throw Exception("Result of string to float conversion is NaN");
- }
-
- return result;
-}
-
-double StringView::ParseToDouble(Index* processed_characters_count,
- StringToNumberFlag flags) const {
- int pcc;
- auto result = CreateStringToDoubleConverter(flags).StringToDouble(
- reinterpret_cast<const uc16*>(ptr_), static_cast<int>(size_), &pcc);
- if (processed_characters_count != nullptr) {
- *processed_characters_count = pcc;
- }
-
- if (flags & StringToNumberFlags::kThrowOnError && std::isnan(result)) {
- throw Exception("Result of string to double conversion is NaN");
- }
-
- return result;
-}
-
-std::vector<float> StringView::ParseToFloatList(value_type separator) const {
- std::vector<float> result;
- auto list = Split(separator, true);
- for (auto& item : list) {
- auto value = item.ParseToFloat();
- if (std::isnan(value)) {
- throw Exception("Invalid double value.");
- }
- result.push_back(value);
- }
- return result;
-}
-
-std::vector<double> StringView::ParseToDoubleList(value_type separator) const {
- std::vector<double> result;
- auto list = Split(separator, true);
- for (auto& item : list) {
- auto value = item.ParseToDouble();
- if (std::isnan(value)) {
- throw Exception("Invalid double value.");
- }
- result.push_back(value);
- }
- return result;
-}
-
String ToLower(StringView s) {
String result;
for (auto c : s) result.push_back(ToLower(c));
diff --git a/src/base/StringToNumberConverter.cpp b/src/base/StringToNumberConverter.cpp
deleted file mode 100644
index f7516630..00000000
--- a/src/base/StringToNumberConverter.cpp
+++ /dev/null
@@ -1,170 +0,0 @@
-#include "cru/base/StringToNumberConverter.h"
-#include "cru/base/Exception.h"
-
-namespace cru {
-bool StringToIntegerConverter::CheckParams() const {
- return base == 0 || base >= 2 & base <= 36;
-}
-
-static bool IsSpace(char c) {
- return c == ' ' || c == '\t' || c == '\n' || c == '\r';
-}
-
-StringToIntegerResult StringToIntegerConverter::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.Has(StringToNumberFlags::kThrowOnError);
-
- auto const end = str + size;
-
- auto 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("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("Empty string (after reading sign).");
- } else {
- return {false, 0};
- }
- }
-
- int real_base = base;
-
- if (real_base == 0) {
- 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;
- real_base = 16;
- } else if (*current == 'b' || *current == 'B') {
- ++current;
- real_base = 2;
- } else {
- real_base = 8;
- }
- } else {
- real_base = 10;
- }
- }
-
- if (current == end) {
- if (processed_characters_count) {
- *processed_characters_count = 0;
- }
- if (throw_on_error) {
- throw Exception("Empty string (after reading head base indicator).");
- } else {
- return {false, 0};
- }
- }
-
- const bool allow_leading_zero =
- flags.Has(StringToNumberFlags::kAllowLeadingZeroForInteger);
-
- while (current != end && *current == '0') {
- current++;
- }
-
- if (current == end) {
- if (processed_characters_count) {
- *processed_characters_count = current - str;
- }
- return {negate, 0};
- }
-
- const bool allow_trailing_junk =
- flags.Has(StringToNumberFlags::kAllowTrailingJunk);
- const bool allow_trailing_spaces =
- flags.Has(StringToNumberFlags::kAllowTrailingSpaces);
-
- unsigned long long result = 0;
-
- while (current != end) {
- const char c = *current;
- if (c >= '0' && c <= (real_base > 10 ? '9' : real_base + '0' - 1)) {
- result = result * real_base + c - '0';
- current++;
- } else if (real_base > 10 && c >= 'a' && c <= (real_base + 'a' - 10 - 1)) {
- result = result * real_base + c - 'a' + 10;
- current++;
- } else if (real_base > 10 && c >= 'A' && c <= (real_base + 'A' - 10 - 1)) {
- result = result * real_base + c - 'A' + 10;
- current++;
- } 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(std::string("Read invalid character '") + c + "'.");
- } else {
- return {false, 0};
- }
- }
- }
-
- if (allow_trailing_spaces) {
- while (current != end && IsSpace(*current)) {
- current++;
- }
-
- if (current != end) {
- if (processed_characters_count) {
- *processed_characters_count = 0;
- }
- if (throw_on_error) {
- throw Exception("There is trailing junk.");
- } else {
- return {false, 0};
- }
- }
- }
-
- if (processed_characters_count) {
- *processed_characters_count = current - str;
- }
-
- return {negate, result};
-}
-
-} // namespace cru
diff --git a/src/platform/graphics/Geometry.cpp b/src/platform/graphics/Geometry.cpp
index e88577ce..4189ef72 100644
--- a/src/platform/graphics/Geometry.cpp
+++ b/src/platform/graphics/Geometry.cpp
@@ -1,7 +1,7 @@
#include "cru/platform/graphics/Geometry.h"
#include "cru/base/Exception.h"
-#include "cru/base/String.h"
+#include "cru/base/StringUtil.h"
#include "cru/platform/Exception.h"
#include "cru/platform/graphics/Factory.h"
@@ -235,17 +235,15 @@ void IGeometryBuilder::ParseAndApplySvgPathData(std::string_view path_d) {
++position;
}
- Index processed_count = 0;
- auto result = String::FromUtf8(path_d.substr(position))
- .ParseToFloat(&processed_count,
- StringToNumberFlags::kAllowTrailingJunk);
+ auto result = cru::string::ParseToNumber<float>(
+ path_d.substr(position), cru::string::ParseToNumberFlags::AllowTrailingJunk);
- if (std::isnan(result)) throw Exception("Invalid svg path data number.");
+ if (!result.valid) throw Exception("Invalid svg path data number.");
- position += processed_count;
+ position += result.processed_char_count;
- return result;
+ return result.value;
};
auto read_point = [&] {
diff --git a/src/ui/components/Input.cpp b/src/ui/components/Input.cpp
index e75eccc5..0a14c7b8 100644
--- a/src/ui/components/Input.cpp
+++ b/src/ui/components/Input.cpp
@@ -1,8 +1,7 @@
#include "cru/ui/components/Input.h"
-#include "cru/base/StringToNumberConverter.h"
+#include "cru/base/StringUtil.h"
#include "cru/ui/controls/Control.h"
-#include <cmath>
#include <optional>
#include <string>
@@ -44,18 +43,18 @@ InputValidateResult Input::GetLastValidateResult() const {
}
InputValidateResult FloatInputValidator::Validate(std::string_view text) const {
- auto result = String::FromUtf8(text).ParseToFloat(
- nullptr, StringToNumberFlags::kAllowLeadingSpaces &
- StringToNumberFlags::kAllowTrailingSpaces);
- if (std::isnan(result)) {
+ auto result = cru::string::ParseToNumber<float>(
+ text, cru::string::ParseToNumberFlags::AllowLeadingSpaces |
+ cru::string::ParseToNumberFlags::AllowTrailingSpaces);
+ if (!result.valid) {
return InputValidateResult{false, "Invalid number."};
}
- if (min && result < *min) {
+ if (min && result.value < *min) {
return InputValidateResult{false, "Value is less than minimum."};
}
- if (max && result > *max) {
+ if (max && result.value > *max) {
return InputValidateResult{false, "Value is greater than maximum."};
}
@@ -67,9 +66,11 @@ FloatInput::FloatInput() {
ChangeEvent()->AddHandler([this](const InputChangeEventArgs& args) {
if (args.valid) {
- value_ = String::FromUtf8(args.text).ParseToFloat(
- nullptr, StringToNumberFlags::kAllowLeadingSpaces &
- StringToNumberFlags::kAllowTrailingSpaces);
+ value_ = cru::string::ParseToNumber<float>(
+ args.text,
+ cru::string::ParseToNumberFlags::AllowLeadingSpaces |
+ cru::string::ParseToNumberFlags::AllowTrailingSpaces)
+ .value;
}
});
}
diff --git a/src/ui/mapper/ColorMapper.cpp b/src/ui/mapper/ColorMapper.cpp
index 82e9dfbc..515eba97 100644
--- a/src/ui/mapper/ColorMapper.cpp
+++ b/src/ui/mapper/ColorMapper.cpp
@@ -1,6 +1,5 @@
#include "cru/ui/mapper/ColorMapper.h"
-
-#include <cru/base/String.h>
+#include "cru/base/StringUtil.h"
namespace cru::ui::mapper {
bool ColorMapper::XmlElementIsOfThisType(xml::XmlElementNode* node) {
@@ -25,7 +24,8 @@ Color ColorMapper::DoMapFromXml(xml::XmlElementNode* node) {
auto alpha_value_attr =
node->GetOptionalAttributeValueCaseInsensitive("alpha");
if (alpha_value_attr) {
- result.alpha = String::FromUtf8(*alpha_value_attr).ParseToDouble() * 255;
+ result.alpha =
+ cru::string::ParseToNumber<double>(*alpha_value_attr).value * 255;
}
return result;
diff --git a/src/ui/mapper/FontMapper.cpp b/src/ui/mapper/FontMapper.cpp
index 1f749513..a0dcdd9f 100644
--- a/src/ui/mapper/FontMapper.cpp
+++ b/src/ui/mapper/FontMapper.cpp
@@ -1,6 +1,7 @@
#include "cru/ui/mapper/FontMapper.h"
#include "../Helper.h"
#include "cru/base/String.h"
+#include "cru/base/StringUtil.h"
#include "cru/platform/graphics/Factory.h"
namespace cru::ui::mapper {
@@ -15,7 +16,8 @@ std::shared_ptr<platform::graphics::IFont> FontMapper::DoMapFromXml(
auto font_family = font_family_attr.value_or("");
auto font_size =
- font_size_attr ? String::FromUtf8(*font_size_attr).ParseToFloat() : 24.0f;
+ font_size_attr ? cru::string::ParseToNumber<float>(*font_size_attr).value
+ : 24.0f;
return GetGraphicsFactory()->CreateFont(font_family, font_size);
}
diff --git a/src/ui/mapper/MeasureLengthMapper.cpp b/src/ui/mapper/MeasureLengthMapper.cpp
index 9f5c2a26..d36afb0e 100644
--- a/src/ui/mapper/MeasureLengthMapper.cpp
+++ b/src/ui/mapper/MeasureLengthMapper.cpp
@@ -15,7 +15,7 @@ render::MeasureLength MeasureLengthMapper::DoMapFromString(std::string str) {
if (cru::string::CaseInsensitiveCompare(str, "unspecified") == 0) {
return render::MeasureLength::NotSpecified();
}
- auto value = String::FromUtf8(str).ParseToFloat();
+ auto value = cru::string::ParseToNumber<float>(str).value;
if (value < 0) {
return render::MeasureLength::NotSpecified();
}
diff --git a/src/ui/mapper/PointMapper.cpp b/src/ui/mapper/PointMapper.cpp
index 12f000ef..a63e1b9e 100644
--- a/src/ui/mapper/PointMapper.cpp
+++ b/src/ui/mapper/PointMapper.cpp
@@ -1,6 +1,5 @@
#include "cru/ui/mapper/PointMapper.h"
-
-#include <cru/base/String.h>
+#include "cru/base/StringUtil.h"
namespace cru::ui::mapper {
bool PointMapper::XmlElementIsOfThisType(xml::XmlElementNode* node) {
@@ -8,7 +7,7 @@ bool PointMapper::XmlElementIsOfThisType(xml::XmlElementNode* node) {
}
Point PointMapper::DoMapFromString(std::string str) {
- std::vector<float> values = String::FromUtf8(str).ParseToFloatList();
+ std::vector<float> values = cru::string::ParseToNumberList<float>(str);
if (values.size() == 2) {
return {values[0], values[1]};
} else if (values.size() == 1) {
diff --git a/src/ui/mapper/SizeMapper.cpp b/src/ui/mapper/SizeMapper.cpp
index de8a0ded..d07d937d 100644
--- a/src/ui/mapper/SizeMapper.cpp
+++ b/src/ui/mapper/SizeMapper.cpp
@@ -1,6 +1,5 @@
#include "cru/ui/mapper/SizeMapper.h"
-
-#include <cru/base/String.h>
+#include "cru/base/StringUtil.h"
namespace cru::ui::mapper {
bool SizeMapper::XmlElementIsOfThisType(xml::XmlElementNode* node) {
@@ -8,7 +7,7 @@ bool SizeMapper::XmlElementIsOfThisType(xml::XmlElementNode* node) {
}
Size SizeMapper::DoMapFromString(std::string str) {
- std::vector<float> values = String::FromUtf8(str).ParseToFloatList();
+ std::vector<float> values = cru::string::ParseToNumberList<float>(str);
if (values.size() == 2) {
return {values[0], values[1]};
} else if (values.size() == 1) {
diff --git a/src/ui/mapper/ThicknessMapper.cpp b/src/ui/mapper/ThicknessMapper.cpp
index 96f016a7..e6557689 100644
--- a/src/ui/mapper/ThicknessMapper.cpp
+++ b/src/ui/mapper/ThicknessMapper.cpp
@@ -1,6 +1,6 @@
#include "cru/ui/mapper/ThicknessMapper.h"
+#include "cru/base/StringUtil.h"
#include "cru/xml/XmlNode.h"
-#include "cru/base/String.h"
namespace cru::ui::mapper {
bool ThicknessMapper::XmlElementIsOfThisType(xml::XmlElementNode* node) {
@@ -8,7 +8,7 @@ bool ThicknessMapper::XmlElementIsOfThisType(xml::XmlElementNode* node) {
}
Thickness ThicknessMapper::DoMapFromString(std::string str) {
- std::vector<float> values = String::FromUtf8(str).ParseToFloatList();
+ std::vector<float> values = cru::string::ParseToNumberList<float>(str);
if (values.size() == 4) {
return Thickness(values[0], values[1], values[2], values[3]);
} else if (values.size() == 2) {
diff --git a/test/base/CMakeLists.txt b/test/base/CMakeLists.txt
index 95313ccb..13a0a2cb 100644
--- a/test/base/CMakeLists.txt
+++ b/test/base/CMakeLists.txt
@@ -4,7 +4,6 @@ add_executable(CruBaseTest
PropertyTreeTest.cpp
SelfResolvableTest.cpp
StringTest.cpp
- StringToNumberConverterTest.cpp
StringUtilTest.cpp
SubProcessTest.cpp
)
diff --git a/test/base/StringTest.cpp b/test/base/StringTest.cpp
index 9ed351e5..65fe0a99 100644
--- a/test/base/StringTest.cpp
+++ b/test/base/StringTest.cpp
@@ -84,29 +84,3 @@ TEST_CASE("String FromUtf8", "[string]") {
REQUIRE(cru::String::FromUtf8(utf8_text) == utf16_text);
}
-
-TEST_CASE("StringView ParseToDouble", "[string]") {
- using cru::StringToNumberFlags;
- using cru::StringView;
- REQUIRE(StringView(u"3.14159").ParseToDouble() == 3.14159);
- REQUIRE(
- StringView(u" 3.14159")
- .ParseToDouble(nullptr, StringToNumberFlags::kAllowLeadingSpaces) ==
- 3.14159);
- REQUIRE(StringView(u" 3.14159 ")
- .ParseToDouble(nullptr,
- StringToNumberFlags::kAllowLeadingSpaces |
- StringToNumberFlags::kAllowTrailingSpaces) ==
- 3.14159);
-}
-
-TEST_CASE("String ParseToDoubleList", "[string]") {
- using cru::StringView;
-
- auto list = StringView(u" 1.23 2.34 3.45 ").ParseToDoubleList();
-
- REQUIRE(list.size() == 3);
- REQUIRE(list[0] == 1.23);
- REQUIRE(list[1] == 2.34);
- REQUIRE(list[2] == 3.45);
-}
diff --git a/test/base/StringToNumberConverterTest.cpp b/test/base/StringToNumberConverterTest.cpp
deleted file mode 100644
index 82062bdb..00000000
--- a/test/base/StringToNumberConverterTest.cpp
+++ /dev/null
@@ -1,166 +0,0 @@
-#include "cru/base/Exception.h"
-#include "cru/base/StringToNumberConverter.h"
-
-#include <catch2/catch_test_macros.hpp>
-
-TEST_CASE("StringToIntegerConverterImpl Base0", "[string]") {
- using namespace cru;
- StringToIntegerConverter converter({}, 0);
- Index processed_characters_count;
-
- REQUIRE(converter.Parse("12345678", &processed_characters_count) ==
- StringToIntegerResult(false, 12345678));
- REQUIRE(processed_characters_count == 8);
-
- REQUIRE(converter.Parse("0", &processed_characters_count) ==
- StringToIntegerResult(false, 0));
- REQUIRE(processed_characters_count == 1);
-
- REQUIRE(converter.Parse("012", &processed_characters_count) ==
- StringToIntegerResult(false, 012));
- REQUIRE(processed_characters_count == 3);
-
- REQUIRE(converter.Parse("0x12", &processed_characters_count) ==
- StringToIntegerResult(false, 0x12));
- REQUIRE(processed_characters_count == 4);
-
- REQUIRE(converter.Parse("0X12", &processed_characters_count) ==
- StringToIntegerResult(false, 0x12));
- REQUIRE(processed_characters_count == 4);
-
- REQUIRE(converter.Parse("0b101", &processed_characters_count) ==
- StringToIntegerResult(false, 0b101));
- REQUIRE(processed_characters_count == 5);
-
- REQUIRE(converter.Parse("0B101", &processed_characters_count) ==
- StringToIntegerResult(false, 0b101));
- REQUIRE(processed_characters_count == 5);
-
- REQUIRE(converter.Parse("-123", &processed_characters_count) ==
- StringToIntegerResult(true, 123));
- REQUIRE(processed_characters_count == 4);
-
- REQUIRE(converter.Parse("-0x10", &processed_characters_count) ==
- StringToIntegerResult(true, 0x10));
- REQUIRE(processed_characters_count == 5);
-}
-
-TEST_CASE("StringToIntegerConverterImpl Base0ForError", "[string]") {
- using namespace cru;
- StringToIntegerConverter converter({}, 0);
- Index processed_characters_count;
-
- REQUIRE(converter.Parse("a", &processed_characters_count) ==
- StringToIntegerResult(false, 0));
- REQUIRE(processed_characters_count == 0);
-
- REQUIRE(converter.Parse("0a", &processed_characters_count) ==
- StringToIntegerResult(false, 0));
- REQUIRE(processed_characters_count == 0);
-
- REQUIRE(converter.Parse("0x", &processed_characters_count) ==
- StringToIntegerResult(false, 0));
- REQUIRE(processed_characters_count == 0);
-
- REQUIRE(converter.Parse("0xx", &processed_characters_count) ==
- StringToIntegerResult(false, 0));
- REQUIRE(processed_characters_count == 0);
-
- REQUIRE(converter.Parse(" 0", &processed_characters_count) ==
- StringToIntegerResult(false, 0));
- REQUIRE(processed_characters_count == 0);
-
- REQUIRE(converter.Parse("0 ", &processed_characters_count) ==
- StringToIntegerResult(false, 0));
- REQUIRE(processed_characters_count == 0);
-}
-
-TEST_CASE("StringToIntegerConverterImpl ThrowOnErrorFlag", "[string]") {
- using namespace cru;
- StringToIntegerConverter converter(StringToNumberFlags::kThrowOnError, 0);
- Index processed_characters_count;
- REQUIRE_THROWS_AS(converter.Parse("?", &processed_characters_count),
- Exception);
-}
-
-TEST_CASE("StringToIntegerConverterImpl AllowLeadingZeroFlag", "[string]") {
- using namespace cru;
- StringToIntegerConverter converter(
- StringToNumberFlags::kAllowLeadingSpaces, 0);
- Index processed_characters_count;
- REQUIRE(converter.Parse(" 123", &processed_characters_count) ==
- StringToIntegerResult(false, 123));
- REQUIRE(processed_characters_count == 6);
-}
-
-TEST_CASE("StringToIntegerConverterImpl AllowTrailingZeroFlag", "[string]") {
- using namespace cru;
- StringToIntegerConverter converter(
- StringToNumberFlags::kAllowTrailingSpaces, 0);
- Index processed_characters_count;
- REQUIRE(converter.Parse("123 ", &processed_characters_count) ==
- StringToIntegerResult(false, 123));
- REQUIRE(processed_characters_count == 6);
-}
-
-TEST_CASE("StringToIntegerConverterImpl AllowTrailingJunk", "[string]") {
- using namespace cru;
- StringToIntegerConverter converter(
- StringToNumberFlags::kAllowTrailingJunk, 0);
- Index processed_characters_count;
- REQUIRE(converter.Parse("123 12", &processed_characters_count) ==
- StringToIntegerResult(false, 123));
- REQUIRE(processed_characters_count == 3);
-}
-
-TEST_CASE("StringToIntegerConverterImpl AllowLeadingZeroForInteger",
- "[string]") {
- using namespace cru;
- StringToIntegerConverter converter(
- StringToNumberFlags::kAllowLeadingZeroForInteger, 0);
- Index processed_characters_count;
- REQUIRE(converter.Parse("0x0012", &processed_characters_count) ==
- StringToIntegerResult(false, 0x12));
- REQUIRE(processed_characters_count == 6);
-
- REQUIRE(converter.Parse("000011", &processed_characters_count) ==
- StringToIntegerResult(false, 000011));
- REQUIRE(processed_characters_count == 6);
-
- REQUIRE(converter.Parse("0b0011", &processed_characters_count) ==
- StringToIntegerResult(false, 0b0011));
- REQUIRE(processed_characters_count == 6);
-}
-
-TEST_CASE("StringToIntegerConverterImpl CompositeFlags", "[string]") {
- using namespace cru;
- StringToIntegerConverter converter(
- StringToNumberFlags::kAllowLeadingSpaces |
- StringToNumberFlags::kAllowTrailingJunk |
- StringToNumberFlags::kAllowLeadingZeroForInteger,
- 0);
- Index processed_characters_count;
-
- REQUIRE(converter.Parse(" 0x00123!!!", &processed_characters_count) ==
- StringToIntegerResult(false, 0x00123));
- REQUIRE(processed_characters_count == 10);
-}
-
-TEST_CASE("StringToIntegerConverterImpl OtherBase", "[string]") {
- using namespace cru;
- StringToIntegerConverter converter({}, 7);
- Index processed_characters_count;
-
- REQUIRE(converter.Parse("12", &processed_characters_count) ==
- StringToIntegerResult(false, 9));
- REQUIRE(processed_characters_count == 2);
-
- REQUIRE(converter.Parse("-12", &processed_characters_count) ==
- StringToIntegerResult(true, 9));
- REQUIRE(processed_characters_count == 3);
-
- converter.base = 11;
- REQUIRE(converter.Parse("1a", &processed_characters_count) ==
- StringToIntegerResult(false, 21));
- REQUIRE(processed_characters_count == 2);
-}
diff --git a/test/base/StringUtilTest.cpp b/test/base/StringUtilTest.cpp
index 3ce4cff1..5951531d 100644
--- a/test/base/StringUtilTest.cpp
+++ b/test/base/StringUtilTest.cpp
@@ -101,6 +101,118 @@ TEST_CASE("StringUtil Utf16CodePointIterator", "[string]") {
REQUIRE(code_points == expected_code_points);
}
+TEST_CASE("ParseToNumber Work", "[string]") {
+ using namespace cru::string;
+
+ auto r1 = ParseToNumber<int>("123");
+ REQUIRE(r1.valid);
+ REQUIRE(r1.value == 123);
+ REQUIRE(r1.processed_char_count == 3);
+
+ auto r2 = ParseToNumber<int>("123.123");
+ REQUIRE(!r2.valid);
+
+ auto r3 = ParseToNumber<float>("123.123");
+ REQUIRE(r3.valid);
+ REQUIRE(r3.value == 123.123f);
+ REQUIRE(r3.processed_char_count == 7);
+
+ auto r4 = ParseToNumber<float>("a123");
+ REQUIRE(!r4.valid);
+}
+
+TEST_CASE("ParseToNumber AllowLeadingZeroFlag", "[string]") {
+ using namespace cru::string;
+
+ auto r1 = ParseToNumber<int>(" 123");
+ REQUIRE(!r1.valid);
+
+ auto r2 = ParseToNumber<int>(" 123", ParseToNumberFlags::AllowLeadingSpaces);
+ REQUIRE(r2.valid);
+ REQUIRE(r2.value == 123);
+ REQUIRE(r2.processed_char_count == 5);
+
+ auto r3 = ParseToNumber<float>(" 123.123");
+ REQUIRE(!r3.valid);
+
+ auto r4 =
+ ParseToNumber<float>(" 123.123", ParseToNumberFlags::AllowLeadingSpaces);
+ REQUIRE(r4.valid);
+ REQUIRE(r4.value == 123.123f);
+ REQUIRE(r4.processed_char_count == 9);
+}
+
+TEST_CASE("StringToIntegerConverterImpl AllowTrailingSpacesFlag", "[string]") {
+ using namespace cru::string;
+
+ auto r1 = ParseToNumber<int>("123 ");
+ REQUIRE(!r1.valid);
+
+ auto r2 =
+ ParseToNumber<int>("123 ", ParseToNumberFlags::AllowTrailingSpaces);
+ REQUIRE(r2.valid);
+ REQUIRE(r2.value == 123);
+ REQUIRE(r2.processed_char_count == 3);
+
+ auto r3 = ParseToNumber<float>("123.123 ");
+ REQUIRE(!r3.valid);
+
+ auto r4 = ParseToNumber<float>("123.123 ",
+ ParseToNumberFlags::AllowTrailingSpaces);
+ REQUIRE(r4.valid);
+ REQUIRE(r4.value == 123.123f);
+ REQUIRE(r4.processed_char_count == 7);
+}
+
+TEST_CASE("StringToIntegerConverterImpl AllowTrailingJunk", "[string]") {
+ using namespace cru::string;
+
+ auto r1 = ParseToNumber<int>("123ab");
+ REQUIRE(!r1.valid);
+
+ auto r2 = ParseToNumber<int>("123ab", ParseToNumberFlags::AllowTrailingJunk);
+ REQUIRE(r2.valid);
+ REQUIRE(r2.value == 123);
+ REQUIRE(r2.processed_char_count == 3);
+
+ auto r3 = ParseToNumber<float>("123.123ab");
+ REQUIRE(!r3.valid);
+
+ auto r4 =
+ ParseToNumber<float>("123.123ab", ParseToNumberFlags::AllowTrailingJunk);
+ REQUIRE(r4.valid);
+ REQUIRE(r4.value == 123.123f);
+ REQUIRE(r4.processed_char_count == 7);
+}
+
+TEST_CASE("StringToIntegerConverterImpl CompositeFlags", "[string]") {
+ using namespace cru::string;
+
+ auto r1 =
+ ParseToNumber<int>(" 123ab", ParseToNumberFlags::AllowLeadingSpaces |
+ ParseToNumberFlags::AllowTrailingJunk);
+ REQUIRE(r1.valid);
+ REQUIRE(r1.value == 123);
+ REQUIRE(r1.processed_char_count == 5);
+
+ auto r2 = ParseToNumber<float>(" 123.123ab",
+ ParseToNumberFlags::AllowLeadingSpaces |
+ ParseToNumberFlags::AllowTrailingJunk);
+ REQUIRE(r2.valid);
+ REQUIRE(r2.value == 123.123f);
+ REQUIRE(r2.processed_char_count == 9);
+}
+
+TEST_CASE("String ParseToNumberList", "[string]") {
+ using namespace cru::string;
+
+ auto r1 = ParseToNumberList<int>("123 456 789");
+ REQUIRE(r1 == std::vector<int>{123, 456, 789});
+
+ auto r2 = ParseToNumberList<float>("1.1 2.2 3.3");
+ REQUIRE(r2 == std::vector<float>{1.1f, 2.2f, 3.3f});
+}
+
// TEST(WinString, IndexUtf8ToUtf16) {
// using cru::platform::win::IndexUtf8ToUtf16;
// std::string_view utf8_string = "aπ你🤣!";