aboutsummaryrefslogtreecommitdiff
path: root/include
diff options
context:
space:
mode:
Diffstat (limited to 'include')
-rw-r--r--include/cru/base/String.h47
-rw-r--r--include/cru/base/StringToNumberConverter.h106
-rw-r--r--include/cru/base/StringUtil.h88
3 files changed, 88 insertions, 153 deletions
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>