From 5cc7cbc3704ec255eb5d0ac53b2cc0fcb1221d63 Mon Sep 17 00:00:00 2001 From: Dominick Allen Date: Wed, 23 Oct 2024 13:21:10 -0500 Subject: String conversion and parsing format spec. --- source/fud_string_view.cpp | 701 ++------------------------------------------- 1 file changed, 19 insertions(+), 682 deletions(-) (limited to 'source/fud_string_view.cpp') diff --git a/source/fud_string_view.cpp b/source/fud_string_view.cpp index 1cc73a6..090dd6d 100644 --- a/source/fud_string_view.cpp +++ b/source/fud_string_view.cpp @@ -1,5 +1,5 @@ /* - * LibFud + * libfud * Copyright 2024 Dominick Allen * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -61,7 +61,7 @@ Result StringView::skipWhitespace() return RetType::error(FudStatus::NullPointer); } size_t index = 0; - while (m_length > 0 && charIsSpace(static_cast(m_data[0]))) { + while (m_length > 0 && classify::isSpace(static_cast(m_data[0]))) { m_data++; m_length--; index++; @@ -78,7 +78,7 @@ Result StringView::trimWhitespace() } size_t count = 0; - while (m_length > 0 && charIsSpace(static_cast(m_data[m_length - 1]))) { + while (m_length > 0 && classify::isSpace(static_cast(m_data[m_length - 1]))) { m_length--; count++; } @@ -120,6 +120,19 @@ void StringView::advanceUnsafe(size_t size) m_data += size; } +FudStatus skipWhitespace(StringView& view, size_t& skipIndex) +{ + auto skipResult = view.skipWhitespace(); + if (skipResult.isError()) { + return skipResult.getError(); + } + skipIndex = skipResult.getOkay(); + if (view.length() < 1) { + return FudStatus::ArgumentInvalid; + } + return FudStatus::Success; +} + #if 0 FudStatus fud_string_truncate(ExtBasicString* source, ssize_t newLength) @@ -134,7 +147,7 @@ FudStatus fud_string_truncate(ExtBasicString* source, ssize_t newLength) if ((newLength > 0 && static_cast(newLength) > source->m_length) || (static_cast(-newLength) > source->m_length)) { - return FudStatus::InvalidInput; + return FudStatus::ArgumentInvalid; } if (newLength < 0) { @@ -158,10 +171,10 @@ FudStatus fud_string_reverse_substring(ExtBasicString* source, StringView subStr { auto dataOffset = subString.data - source->m_data; if (dataOffset < 0 || static_cast(dataOffset) > source->m_length) { - return FudStatus::InvalidInput; + return FudStatus::ArgumentInvalid; } if (static_cast(dataOffset) + subString.length > source->m_length) { - return FudStatus::InvalidInput; + return FudStatus::ArgumentInvalid; } if (source == nullptr || source->m_data == nullptr) { @@ -476,682 +489,6 @@ FudStatus fud_string_find_substring(StringView haystack, StringView needle, Stri return ExtNotFound; } -FudStatus skipWhitespace(StringView& view, size_t& skipIndex) -{ - auto skipResult = view.skipWhitespace(); - if (skipResult.isError()) { - return skipResult.getError(); - } - skipIndex = skipResult.getOkay(); - if (view.length < 1) { - return FudStatus::InvalidInput; - } - return FudStatus::Success; -} - -FudStatus fud_string_view_skip_whitespace(StringView* view) -{ - if (view == nullptr) { - return FudStatus::NullPointer; - } - - StringView sView{*view}; - auto skipResult = sView.skipWhitespace(); - if (skipResult.isError()) { - return skipResult.getError(); - } - view->data = sView.data; - view->length = sView.length; - return FudStatus::Success; -} - -FudStatus fud_string_view_trim_whitespace(StringView* view) -{ - if (view == nullptr) { - return FudStatus::NullPointer; - } - - StringView sView{*view}; - auto skipResult = sView.trimWhitespace(); - if (skipResult.isError()) { - return skipResult.getError(); - } - view->data = sView.data; - view->length = sView.length; - return FudStatus::Success; -} - -namespace impl { -constexpr Array AsciiLookup{ - {-1, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, - -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, - -2, -2, -2, -2, -2, -2, -2, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, - 32, 33, 34, 35, -2, -2, -2, -2, -2, -2, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, - 29, 30, 31, 32, 33, 34, 35, -2, -2, -2, -2, -2, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, - -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, - -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, - -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, - -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3}}; - -// NOLINTBEGIN(readability-magic-numbers) -static_assert(AsciiLookup[static_cast('0')] == 0); -static_assert(AsciiLookup[static_cast('9')] == 9); -static_assert(AsciiLookup[static_cast('a')] == 10); -static_assert(AsciiLookup[static_cast('A')] == 10); -static_assert(AsciiLookup[static_cast('f')] == 15); -static_assert(AsciiLookup[static_cast('F')] == 15); -static_assert(AsciiLookup[127] == -2); -static_assert(AsciiLookup[128] == -3); -static_assert(AsciiLookup[255] == -3); -// NOLINTEND(readability-magic-numbers) - -FudStatus determineRadix(StringView input, uint8_t& radix, size_t& index) -{ - if (input.length < 1) { - return FudStatus::InvalidInput; - } - - if (input.length == 1 && input.data[0] == '0') { - radix = ExtRadixOctal; - return FudStatus::Success; - } - - if (input.length == 1) { - radix = ExtRadixDecimal; - return FudStatus::Success; - } - - if (input.data[0] == '0' && (input.data[1] == 'x' || input.data[1] == 'X')) { - radix = ExtRadixHexadecimal; - index += 2; - return FudStatus::Success; - } - - if (input.data[0] == '0') { - auto nextChar = input.data[1]; - auto nextVal = AsciiLookup[nextChar]; - if (nextVal >= 0 && nextVal < ExtRadixOctal) { - radix = ExtRadixOctal; - return FudStatus::Success; - } - if (nextVal >= ExtRadixOctal) { - return FudStatus::InvalidInput; - } - } - - radix = ExtRadixDecimal; - return FudStatus::Success; -} - -FudStatus getRadix(StringView& view, uint8_t& radix, size_t& skipIndex) -{ - if (radix == 0) { - size_t radixIndex = 0; - auto status = determineRadix(view, radix, radixIndex); - if (status != FudStatus::Success) { - return status; - } - skipIndex += radixIndex; - view.data += radixIndex; - view.length -= radixIndex; - } else if (radix == ExtRadixHexadecimal && view.length > 2 && (view.data[1] == 'x' || view.data[1] == 'X')) { - skipIndex += 2; - view.data += 2; - view.length -= 2; - } - return FudStatus::Success; -} - -FudStatus checkNegative(StringView& view, bool& isNegative, size_t& skipIndex) -{ - isNegative = view.data[0] == '-'; - if (isNegative && view.length == 1) { - return FudStatus::InvalidInput; - } - if (isNegative) { - skipIndex += 1; - view.data++; - view.length--; - } - return FudStatus::Success; -} - -FudStatus checkPlusSigned(StringView& view, size_t& skipIndex) -{ - auto isPlusSigned = view.data[0] == '+'; - if (isPlusSigned && view.length == 1) { - return FudStatus::InvalidInput; - } - if (isPlusSigned) { - skipIndex += 1; - view.data++; - view.length--; - } - return FudStatus::Success; -} - -template -FudStatus stringViewToUnsignedInteger(StringView input, T& number, uint8_t specifiedRadix, size_t& index) -{ - if (input.data == nullptr) { - return FudStatus::NullPointer; - } - - if (specifiedRadix == 1 || specifiedRadix > ExtMaxRadix || input.length < 1) { - return FudStatus::InvalidInput; - } - - uint8_t radix = specifiedRadix; - - StringView view{input}; - size_t skipIndex = 0; - auto status = skipWhitespace(view, skipIndex); - if (status != FudStatus::Success) { - return status; - } - - status = checkPlusSigned(view, skipIndex); - if (status != FudStatus::Success) { - return FudStatus::InvalidInput; - } - - status = getRadix(view, radix, skipIndex); - - T num = 0; - size_t digitIndex = 0; - - while (digitIndex < view.length) { - auto digitResult = impl::AsciiLookup[view.data[digitIndex]]; - if (digitResult >= radix || digitResult < 0) { - break; - } - - auto digit = static_cast(digitResult); - if (std::numeric_limits::max() / radix < num) { - return FudStatus::InvalidInput; - } - num *= radix; - if (std::numeric_limits::max() - digit < num) { - return FudStatus::InvalidInput; - } - num += digit; - digitIndex++; - } - if (digitIndex < 1) { - return FudStatus::InvalidInput; - } - - index = skipIndex + digitIndex; - number = num; - - return FudStatus::Success; -} - -template -FudStatus stringViewToUnsignedInteger(StringView input, T* number, uint8_t specifiedRadix, size_t* index) -{ - if (anyAreNull(input.data, number)) { - return FudStatus::NullPointer; - } - - size_t localIndex = 0; - - auto status = stringViewToUnsignedInteger(input, *number, specifiedRadix, localIndex); - if (status == FudStatus::Success && index != nullptr) { - *index = localIndex; - } - return status; -} - -template -FudStatus viewToSignedIntPositive(StringView view, uint8_t radix, size_t& digitIndex, T& num) -{ - digitIndex = 0; - while (digitIndex < view.length) { - int8_t digitResult = impl::AsciiLookup[view.data[digitIndex]]; - if (digitResult >= radix) { - return FudStatus::InvalidInput; - } - if (digitResult < 0) { - break; - } - auto digit = static_cast(digitResult); - if (std::numeric_limits::max() / radix < num) { - return FudStatus::InvalidInput; - } - num = static_cast(num * radix); - if (std::numeric_limits::max() - digit < num) { - return FudStatus::InvalidInput; - } - num = static_cast(num + digit); - digitIndex++; - } - - return FudStatus::Success; -} - -template -FudStatus viewToSignedIntNegative(StringView view, uint8_t radix, size_t& digitIndex, T& num) -{ - digitIndex = 0; - while (digitIndex < view.length) { - int8_t digitResult = impl::AsciiLookup[view.data[digitIndex]]; - if (digitResult >= radix) { - return FudStatus::InvalidInput; - } - if (digitResult < 0) { - break; - } - auto digit = static_cast(digitResult); - if ((std::numeric_limits::min() / radix > num)) { - return FudStatus::InvalidInput; - } - num = static_cast(num * radix); - if (std::numeric_limits::min() + digit > num) { - return FudStatus::InvalidInput; - } - num = static_cast(num - digit); - digitIndex++; - } - - return FudStatus::Success; -} - -template -FudStatus stringViewToSignedInteger(StringView input, T& number, uint8_t specifiedRadix, size_t& index) -{ - if (input.data == nullptr) { - return FudStatus::NullPointer; - } - - auto radix = specifiedRadix; - - StringView view{input}; - size_t skipIndex = 0; - auto status = skipWhitespace(view, skipIndex); - if (status != FudStatus::Success) { - return status; - } - - bool isNegative = false; - status = checkNegative(view, isNegative, skipIndex); - if (status != FudStatus::Success) { - return FudStatus::InvalidInput; - } - - if (!isNegative) { - status = checkPlusSigned(view, skipIndex); - if (status != FudStatus::Success) { - return FudStatus::InvalidInput; - } - } - - status = getRadix(view, radix, skipIndex); - - T num = 0; - size_t digitIndex = 0; - - if (isNegative) { - status = viewToSignedIntNegative(view, radix, digitIndex, num); - } else { - status = viewToSignedIntPositive(view, radix, digitIndex, num); - } - if (status != FudStatus::Success) { - return status; - } - - if (digitIndex < 1) { - return FudStatus::InvalidInput; - } - - index = skipIndex + digitIndex; - number = num; - return FudStatus::Success; -} - -template -FudStatus stringViewToSignedInteger(StringView input, T* number, uint8_t specifiedRadix, size_t* index) -{ - if (anyAreNull(input.data, number)) { - return FudStatus::NullPointer; - } - - if (specifiedRadix == 1 || specifiedRadix > ExtMaxRadix || input.length < 1) { - return FudStatus::InvalidInput; - } - - size_t localIndex = 0; - auto status = stringViewToSignedInteger(input, *number, specifiedRadix, localIndex); - if (status == FudStatus::Success && index != nullptr) { - *index = localIndex; - } - return status; -} - -} // namespace impl - -FudStatus fud_string_to_uint8(StringView input, uint8_t* number, uint8_t specifiedRadix, size_t* index) -{ - return impl::stringViewToUnsignedInteger(input, number, specifiedRadix, index); -} - -FudStatus fud_string_to_uint16(StringView input, uint16_t* number, uint8_t specifiedRadix, size_t* index) -{ - return impl::stringViewToUnsignedInteger(input, number, specifiedRadix, index); -} - -FudStatus fud_string_to_uint32(StringView input, uint32_t* number, uint8_t specifiedRadix, size_t* index) -{ - return impl::stringViewToUnsignedInteger(input, number, specifiedRadix, index); -} - -FudStatus fud_string_to_uint64(StringView input, uint64_t* number, uint8_t specifiedRadix, size_t* index) -{ - return impl::stringViewToUnsignedInteger(input, number, specifiedRadix, index); -} - -FudStatus fud_string_to_int8(StringView input, int8_t* number, uint8_t specifiedRadix, size_t* index) -{ - return impl::stringViewToSignedInteger(input, number, specifiedRadix, index); -} - -FudStatus fud_string_to_int16(StringView input, int16_t* number, uint8_t specifiedRadix, size_t* index) -{ - return impl::stringViewToSignedInteger(input, number, specifiedRadix, index); -} - -FudStatus fud_string_to_int32(StringView input, int32_t* number, uint8_t specifiedRadix, size_t* index) -{ - return impl::stringViewToSignedInteger(input, number, specifiedRadix, index); -} - -FudStatus fud_string_to_int64(StringView input, int64_t* number, uint8_t specifiedRadix, size_t* index) -{ - return impl::stringViewToSignedInteger(input, number, specifiedRadix, index); -} - -namespace impl { - -template -bool isNanOrInf(T& num, StringView& view, T& sign, size_t& digitIndex) -{ - if (view.length >= 3) { - Array letters{{view.data[0], view.data[1], view.data[2]}}; - mapMut(letters, fud_char_to_lower); - if (letters[0] == 'i' && letters[1] == 'n' && letters[2] == 'f') { - num = sign * std::numeric_limits::infinity(); - digitIndex = 3; - return true; - } - if (letters[0] == 'n' && letters[1] == 'a' && letters[2] == 'n') { - num = std::numeric_limits::quiet_NaN(); - ; - digitIndex = 3; - return true; - } - } - return false; -} - -template -FudStatus getWhole( - const StringView view, - size_t& digitIndex, - T& num, - T sign, - uint8_t radix, - bool& foundDecimal, - bool& foundExponent) -{ - while (digitIndex < view.length) { - auto nextChar = view.data[digitIndex]; - if (nextChar == '.') { - foundDecimal = true; - digitIndex++; - break; - } - - if (radix == ExtRadixDecimal && (nextChar == 'e' || nextChar == 'E')) { - foundExponent = true; - digitIndex++; - break; - } - - auto digitResult = impl::AsciiLookup[nextChar]; - if (digitResult >= radix) { - return FudStatus::InvalidInput; - } - if (digitResult < 0) { - break; - } - auto digit = static_cast(digitResult) * sign; - num *= static_cast(radix); - - num += digit; - digitIndex++; - } - return FudStatus::Success; -} - -template -FudStatus getExponent(StringView view, size_t& digitIndex, T& num, uint8_t radix) -{ - int32_t exponent{}; - StringView tempView{view.length - digitIndex, view.data + digitIndex}; - size_t exponentLength{}; - auto status = tempView.toInt32(exponent, ExtRadixDecimal, exponentLength); - if (status != FudStatus::Success) { - return status; - } - digitIndex += exponentLength; - num = num * std::pow(static_cast(radix), static_cast(exponent)); - return FudStatus::Success; -} - -template -FudStatus getFraction(const StringView view, size_t& digitIndex, T& num, T sign, uint8_t radix, bool& foundExponent) -{ - auto radixDiv = 1.0F / static_cast(radix); - while (digitIndex < view.length) { - auto nextChar = view.data[digitIndex]; - if (radix == ExtRadixDecimal && (nextChar == 'e' || nextChar == 'E')) { - foundExponent = true; - digitIndex++; - break; - } - - auto digitResult = impl::AsciiLookup[nextChar]; - if (digitResult >= radix) { - return FudStatus::InvalidInput; - } - if (digitResult < 0) { - break; - } - auto digit = static_cast(digitResult) * sign; - num += digit * radixDiv; - radixDiv /= static_cast(radix); - digitIndex++; - } - return FudStatus::Success; -} - -template -FudStatus stringViewToFloat(StringView input, T& number, size_t& index) -{ - if (input.data == nullptr) { - return FudStatus::NullPointer; - } - - if (input.length < 1) { - return FudStatus::InvalidInput; - } - - uint8_t radix = 0; - - StringView view{input}; - size_t skipIndex = 0; - - auto status = skipWhitespace(view, skipIndex); - if (status != FudStatus::Success) { - return status; - } - - T sign = 1.0; - bool isNegative = false; - status = impl::checkNegative(view, isNegative, skipIndex); - if (status != FudStatus::Success) { - return FudStatus::InvalidInput; - } - - if (!isNegative) { - status = checkPlusSigned(view, skipIndex); - } else { - sign = -1.0; - } - - if (status != FudStatus::Success) { - return FudStatus::InvalidInput; - } - - T num = 0; - size_t digitIndex = 0; - - auto retSuccess = [&]() { - index = skipIndex + digitIndex; - number = num; - return FudStatus::Success; - }; - - if (impl::isNanOrInf(num, view, sign, digitIndex)) { - return retSuccess(); - } - - status = impl::getRadix(view, radix, skipIndex); - if (status != FudStatus::Success) { - return status; - } - - bool foundDecimal = false; - bool foundExponent = false; - status = getWhole(view, digitIndex, num, sign, radix, foundDecimal, foundExponent); - - if (status == FudStatus::Success && foundExponent) { - status = getExponent(view, digitIndex, num, radix); - } - - if (status != FudStatus::Success) { - return status; - } - - if (!foundDecimal) { - if (digitIndex < 1) { - return FudStatus::InvalidInput; - } - - return retSuccess(); - } - - status = getFraction(view, digitIndex, num, sign, radix, foundExponent); - - if (foundExponent) { - status = getExponent(view, digitIndex, num, radix); - if (status != FudStatus::Success) { - return status; - } - } - - if (digitIndex < 1) { - return FudStatus::InvalidInput; - } - - if (std::isinf(num) || std::isnan(num)) // isnan is dubious here - likely unreachable - { - return FudStatus::RangeError; - } - - return retSuccess(); -} - -template -FudStatus stringViewToFloat(StringView input, T* number, size_t* index) -{ - if (anyAreNull(input.data, number)) { - return FudStatus::NullPointer; - } - - size_t localIndex{0}; - auto status = stringViewToFloat(input, *number, localIndex); - - if (status == FudStatus::Success && index != nullptr) { - *index = localIndex; - } - return status; -} - -} // namespace impl - -FudStatus fud_string_to_float(StringView input, float* number, size_t* index) -{ - return impl::stringViewToFloat(input, number, index); -} - -FudStatus fud_string_to_double(StringView input, double* number, size_t* index) -{ - return impl::stringViewToFloat(input, number, index); -} - -namespace fud { - -FudStatus StringView::toUint8(uint8_t& number, uint8_t specifiedRadix, size_t& strLen) const -{ - return ::impl::stringViewToUnsignedInteger(*this, number, specifiedRadix, strLen); -} - -FudStatus StringView::toUint16(uint16_t& number, uint8_t specifiedRadix, size_t& strLen) const -{ - return ::impl::stringViewToUnsignedInteger(*this, number, specifiedRadix, strLen); -} - -FudStatus StringView::toUint32(uint32_t& number, uint8_t specifiedRadix, size_t& strLen) const -{ - return ::impl::stringViewToUnsignedInteger(*this, number, specifiedRadix, strLen); -} - -FudStatus StringView::toUint64(uint64_t& number, uint8_t specifiedRadix, size_t& strLen) const -{ - return ::impl::stringViewToUnsignedInteger(*this, number, specifiedRadix, strLen); -} - -FudStatus StringView::toInt8(int8_t& number, uint8_t specifiedRadix, size_t& strLen) const -{ - return ::impl::stringViewToSignedInteger(*this, number, specifiedRadix, strLen); -} - -FudStatus StringView::toInt16(int16_t& number, uint8_t specifiedRadix, size_t& strLen) const -{ - return ::impl::stringViewToSignedInteger(*this, number, specifiedRadix, strLen); -} - -FudStatus StringView::toInt32(int32_t& number, uint8_t specifiedRadix, size_t& strLen) const -{ - return ::impl::stringViewToSignedInteger(*this, number, specifiedRadix, strLen); -} - -FudStatus StringView::toInt64(int64_t& number, uint8_t specifiedRadix, size_t& strLen) const -{ - return ::impl::stringViewToSignedInteger(*this, number, specifiedRadix, strLen); -} - -FudStatus StringView::toFloat(float& number, size_t& strLen) const -{ - return ::impl::stringViewToFloat(*this, number, strLen); -} - -FudStatus StringView::toDouble(double& number, size_t& strLen) const -{ - return ::impl::stringViewToFloat(*this, number, strLen); -} - #endif } // namespace fud -- cgit v1.2.3