From 3d412e6e7de90c62062fb445461c6077c68c8414 Mon Sep 17 00:00:00 2001 From: John Wellbelove Date: Sun, 18 Sep 2022 09:54:17 +0100 Subject: [PATCH] First implementation ideas --- include/etl/file_error_numbers.h | 1 + include/etl/string_view.h | 8 +- include/etl/to_arithmetic.h | 345 +++++++++++++++++++++++++++++ test/test_string_to_arithmetic.cpp | 68 ++++++ test/vs2019/etl.vcxproj | 2 + test/vs2019/etl.vcxproj.filters | 6 + 6 files changed, 426 insertions(+), 4 deletions(-) create mode 100644 include/etl/to_arithmetic.h create mode 100644 test/test_string_to_arithmetic.cpp diff --git a/include/etl/file_error_numbers.h b/include/etl/file_error_numbers.h index 436778f3..7d6a0e1a 100644 --- a/include/etl/file_error_numbers.h +++ b/include/etl/file_error_numbers.h @@ -99,5 +99,6 @@ SOFTWARE. #define ETL_BYTE_STREAM_FILE_ID "66" #define ETL_BIP_BUFFER_SPSC_ATOMIC_FILE_ID "67" #define ETL_REFERENCE_COUNTED_OBJECT_FILE_ID "68" +#define ETL_TO_ARITHMETIC_FILE_ID "69" #endif diff --git a/include/etl/string_view.h b/include/etl/string_view.h index 3f52133d..8bad7c00 100644 --- a/include/etl/string_view.h +++ b/include/etl/string_view.h @@ -435,7 +435,7 @@ namespace etl ETL_CONSTEXPR14 bool starts_with(etl::basic_string_view view) const { return (size() >= view.size()) && - (compare(0, view.size(), view) == 0); + (compare(0, view.size(), view) == 0); } ETL_CONSTEXPR14 bool starts_with(T c) const @@ -448,7 +448,7 @@ namespace etl size_t lengthtext = TTraits::length(text); return (size() >= lengthtext) && - (compare(0, lengthtext, text) == 0); + (compare(0, lengthtext, text) == 0); } //************************************************************************* @@ -457,7 +457,7 @@ namespace etl ETL_CONSTEXPR14 bool ends_with(etl::basic_string_view view) const { return (size() >= view.size()) && - (compare(size() - view.size(), npos, view) == 0); + (compare(size() - view.size(), npos, view) == 0); } ETL_CONSTEXPR14 bool ends_with(T c) const @@ -471,7 +471,7 @@ namespace etl size_t lengthview = size(); return (lengthview >= lengthtext) && - (compare(lengthview - lengthtext, lengthtext, text) == 0); + (compare(lengthview - lengthtext, lengthtext, text) == 0); } //************************************************************************* diff --git a/include/etl/to_arithmetic.h b/include/etl/to_arithmetic.h new file mode 100644 index 00000000..3aefbe17 --- /dev/null +++ b/include/etl/to_arithmetic.h @@ -0,0 +1,345 @@ + +#include "platform.h" +#include "type_traits.h" +#include "string_view.h" +#include "basic_string.h" +#include "format_spec.h" +#include "radix.h" +#include "optional.h" +#include "string_utilities.h" +#include "iterator.h" +#include "largest.h" +#include "exception.h" +#include "error_handler.h" + +namespace etl +{ + //*************************************************************************** + /// + //*************************************************************************** + class to_arithmetic_exception : public etl::exception + { + public: + + to_arithmetic_exception(string_type reason_, string_type file_name_, numeric_type line_number_) + : exception(reason_, file_name_, line_number_) + { + } + }; + + //*************************************************************************** + /// + //*************************************************************************** + class to_arithmetic_signed_to_unsigned : public to_arithmetic_exception + { + public: + + to_arithmetic_signed_to_unsigned(string_type file_name_, numeric_type line_number_) + : to_arithmetic_exception(ETL_ERROR_TEXT("to arithmetic:signed to unsigned", ETL_TO_ARITHMETIC_FILE_ID"A"), file_name_, line_number_) + { + } + }; + + //*************************************************************************** + /// + //*************************************************************************** + class to_arithmetic_invalid_format : public to_arithmetic_exception + { + public: + + to_arithmetic_invalid_format(string_type file_name_, numeric_type line_number_) + : to_arithmetic_exception(ETL_ERROR_TEXT("to arithmetic:invalid format", ETL_TO_ARITHMETIC_FILE_ID"B"), file_name_, line_number_) + { + } + }; + + namespace private_to_arithmetic + { + template + struct character_set; + + template <> + struct character_set + { + typedef char value_type; + typedef const value_type* string_type; + static ETL_CONSTANT string_type numeric_chars = "0123456789abcdefABCDEF"; + static ETL_CONSTANT value_type positive_char = '+'; + static ETL_CONSTANT value_type negative_char = '-'; + }; + + template <> + struct character_set + { + typedef wchar_t value_type; + typedef const value_type* string_type; + static ETL_CONSTANT string_type numeric_chars = L"0123456789abcdefABCDEF"; + static ETL_CONSTANT value_type positive_char = L'+'; + static ETL_CONSTANT value_type negative_char = L'-'; + }; + + template <> + struct character_set + { + typedef char16_t value_type; + typedef const value_type* string_type; + static ETL_CONSTANT string_type numeric_chars = u"0123456789abcdefABCDEF"; + static ETL_CONSTANT value_type positive_char = u'+'; + static ETL_CONSTANT value_type negative_char = u'-'; + }; + + template <> + struct character_set + { + typedef char32_t value_type; + typedef const value_type* string_type; + static ETL_CONSTANT string_type numeric_chars = U"0123456789abcdefABCDEF"; + static ETL_CONSTANT value_type positive_char = U'+'; + static ETL_CONSTANT value_type negative_char = U'-'; + }; + + static ETL_CONSTANT char binary_length = 2; + static ETL_CONSTANT char octal_length = 8; + static ETL_CONSTANT char decimal_length = 10; + static ETL_CONSTANT char hex_length = 16 + 6; + + //******************************* + template + bool has_valid_characters(etl::basic_string_view view, etl::basic_string_view valid_characters) + { + if (valid_characters.empty()) + { + return false; + } + + size_t position = view.find_first_not_of(valid_characters); + + return position == etl::basic_string_view::npos; + } + + //******************************* + template + char get_digit_value(TChar c) + { + size_t length = etl::strlen(character_set::numeric_chars); + + for (int i = 0; i < length; ++i) + { + if (c == character_set::numeric_chars[i]) + { + return (i < 16) ? i : i - 6; + } + } + + return 0; + } + + //******************************* + template + struct processed_string + { + etl::basic_string_view view; + bool is_negative; + bool is_valid; + }; + + //*************************************************************************** + /// + //*************************************************************************** + template + processed_string preprocess_string(etl::basic_string_view view, const etl::radix::value_type radix) + { + using namespace etl::private_to_arithmetic; + + view = etl::trim_view_whitespace(view); + + const bool has_positive_prefix = (view[0] == character_set::positive_char); + const bool has_negative_prefix = (view[0] == character_set::negative_char); + + // Remove positive or negative prefixes. + if (has_positive_prefix || has_negative_prefix) + { + view.remove_prefix(1); + } + + etl::basic_string_view valid_characters; + + switch (radix) + { + case etl::radix::binary: { valid_characters = etl::basic_string_view(character_set::numeric_chars, binary_length); break; } + case etl::radix::octal: { valid_characters = etl::basic_string_view(character_set::numeric_chars, octal_length); break; } + case etl::radix::decimal: { valid_characters = etl::basic_string_view(character_set::numeric_chars, decimal_length); break; } + case etl::radix::hex: { valid_characters = etl::basic_string_view(character_set::numeric_chars, hex_length); break; } + default: { break; } // No conversion available. + } + + const bool has_valid_prefix = !has_negative_prefix || (radix == etl::radix::decimal); + const bool is_valid = has_valid_prefix && has_valid_characters(view, valid_characters); + + return processed_string{ view, has_negative_prefix, is_valid }; + } + } + + //*************************************************************************** + /// + //*************************************************************************** + template + ETL_CONSTEXPR14 typename etl::enable_if::value, etl::optional >::type + to_integer(etl::basic_string_view view, const etl::radix::value_type radix) + { + using namespace etl::private_to_arithmetic; + + processed_string data = preprocess_string(view, radix); + + etl::optional result; + + // String input was not in a valid format. + if (!data.is_valid) + { + ETL_ASSERT_FAIL_AND_RETURN_VALUE(ETL_ERROR(etl::to_arithmetic_invalid_format), result); + } + + // Can't convert signed to unsigned. + if (data.is_negative && etl::is_unsigned::value) + { + ETL_ASSERT_FAIL_AND_RETURN_VALUE(ETL_ERROR(etl::to_arithmetic_signed_to_unsigned), result); + } + + if (data.is_valid) + { + T value = 0; + + switch (radix) + { + case etl::radix::decimal: + { + for (etl::basic_string_view::const_iterator itr = data.view.begin(); itr != data.view.end(); ++itr) + { + value *= radix; + + char digit = get_digit_value(*itr); + + data.is_negative ? value -= digit : value += digit; + } + + break; + } + + default: + { + int shift = (radix == etl::radix::binary) ? 1 : (radix == etl::radix::octal) ? 3 : 4; + + for (etl::basic_string_view::const_iterator itr = data.view.begin(); itr != data.view.end(); ++itr) + { + value <<= shift; + + char digit = get_digit_value(*itr); + + value = value | digit; + } + + break; + } + + } + + result = value; + } + + return result; + } + + //*************************************************************************** + /// + //*************************************************************************** + template + ETL_CONSTEXPR14 typename etl::enable_if::value, etl::optional >::type + to_integer(const etl::basic_string_view& view) + { + return etl::to_integer(view, etl::radix::decimal); + } + + //*************************************************************************** + /// + //*************************************************************************** + template + ETL_CONSTEXPR14 typename etl::enable_if::value, etl::optional >::type + to_integer(const etl::basic_string_view& view, const etl::basic_format_spec >& spec) + { + return etl::to_integer(view, spec.get_base()); + } + + //*************************************************************************** + /// + //*************************************************************************** + template + ETL_CONSTEXPR14 typename etl::enable_if::value, etl::optional >::type + to_integer(const TChar* cp, size_t length, const etl::radix::value_type radix) + { + return etl::to_integer(etl::basic_string_view(cp, length), radix); + } + + //*************************************************************************** + /// + //*************************************************************************** + template + ETL_CONSTEXPR14 typename etl::enable_if::value, etl::optional >::type + to_integer(const TChar* cp, size_t length) + { + return etl::to_integer(etl::basic_string_view(cp, length), etl::radix::decimal);; + } + + //*************************************************************************** + /// + //*************************************************************************** + template + ETL_CONSTEXPR14 typename etl::enable_if::value, etl::optional >::type + to_integer(const TChar* cp, size_t length, const typename etl::private_basic_format_spec::base_spec& spec) + { + return etl::to_integer(etl::basic_string_view(cp, length), spec.base);; + } + + //*************************************************************************** + /// + //*************************************************************************** + template + ETL_CONSTEXPR14 typename etl::enable_if::value, etl::optional >::type + to_integer(const etl::ibasic_string& str, const etl::radix::value_type radix) + { + return etl::to_integer(etl::basic_string_view(str), radix);; + } + + //*************************************************************************** + /// + //*************************************************************************** + template + ETL_CONSTEXPR14 typename etl::enable_if::value, etl::optional >::type + to_integer(const etl::ibasic_string& str) + { + return etl::to_integer(etl::basic_string_view(str), radix);; + } + + //*************************************************************************** + /// + //*************************************************************************** + template + ETL_CONSTEXPR14 typename etl::enable_if::value, etl::optional >::type + to_integer(const etl::ibasic_string& str, const etl::basic_format_spec >& spec) + { + return etl::to_integer(etl::basic_string_view(str), spec);; + } + + template + ETL_CONSTEXPR14 typename etl::enable_if::value, etl::optional >::type + to_arithmetic(etl::basic_string_view view, const etl::radix::value_type radix) + { + return to_integer(view, radix); + } + + template + ETL_CONSTEXPR14 typename etl::enable_if::value, etl::optional >::type + to_arithmetic(etl::basic_string_view view, const etl::radix::value_type radix) + { + return to_floating_point(view, radix); + } +} diff --git a/test/test_string_to_arithmetic.cpp b/test/test_string_to_arithmetic.cpp new file mode 100644 index 00000000..06f5e5a3 --- /dev/null +++ b/test/test_string_to_arithmetic.cpp @@ -0,0 +1,68 @@ +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl +https://www.etlcpp.com + +Copyright(c) 2019 John Wellbelove + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files(the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +******************************************************************************/ + +#include "unit_test_framework.h" + +#include +#include +#include + +#include "etl/to_arithmetic.h" +#include "etl/string.h" +#include "etl/format_spec.h" + +#undef STR +#define STR(x) x + +namespace +{ + typedef etl::format_spec Format; + + SUITE(test_to_arithmetic) + { + TEST(test_to_integer_from_char_pointer_to_decimals) + { + const char* pp = " -128 "; + const char* pn = " -123"; + + const std::string huge_n("8000000000000001"); + const std::string huge_p("7FFFFFFFFFFFFFFF"); + + //CHECK_EQUAL(int(-128), etl::to_integer(pp, strlen(pp), etl::dec).value()); + + CHECK_EQUAL(std::numeric_limits::min(), etl::to_integer(huge_n.c_str(), huge_n.size(), etl::hex).value()); + CHECK_EQUAL(std::numeric_limits::max(), etl::to_integer(huge_p.c_str(), huge_p.size(), etl::hex).value()); + + //CHECK_EQUAL(123, etl::to_integer(pp, 3, etl::dec)); + //CHECK_EQUAL(123, etl::to_integer(pp, 3, etl::radix::decimal)); + //CHECK_EQUAL(123, etl::to_integer(pp, 3, etl::radix(etl::radix::decimal))); + //CHECK_EQUAL(123, etl::to_integer(pp, 3, 10)); + } + } +} + diff --git a/test/vs2019/etl.vcxproj b/test/vs2019/etl.vcxproj index 2a3fa8ce..b011749c 100644 --- a/test/vs2019/etl.vcxproj +++ b/test/vs2019/etl.vcxproj @@ -2588,6 +2588,7 @@ + @@ -12107,6 +12108,7 @@ + diff --git a/test/vs2019/etl.vcxproj.filters b/test/vs2019/etl.vcxproj.filters index daef9087..29ca6e71 100644 --- a/test/vs2019/etl.vcxproj.filters +++ b/test/vs2019/etl.vcxproj.filters @@ -1338,6 +1338,9 @@ ETL\Private + + ETL\Strings + @@ -3371,6 +3374,9 @@ Tests\Binary + + Tests\Strings +