/* * LibFud * Copyright 2024 Dominick Allen * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "fud_string_view.hpp" #include "fud_string.hpp" namespace fud { StringView::StringView(const String& fudString) noexcept : StringView(fudString.length(), fudString.data()) { } bool StringView::nullTerminated() const { return m_data != nullptr && m_data[m_length] == '\0'; } bool StringView::utf8Valid() const { if (m_data == nullptr) { return false; } for (size_t idx = 0; idx < m_length;) { if (Ascii::valid(m_data[idx])) { idx++; } else if (idx + 1 < m_length && Utf82Byte::valid(m_data[idx], m_data[idx + 1])) { idx += 2; } else if (idx + 2 < m_length && Utf83Byte::valid(m_data[idx], m_data[idx + 1], m_data[idx + 2])) { idx += 3; } else if ( idx + 3 < m_length && Utf84Byte::valid(m_data[idx], m_data[idx + 1], m_data[idx + 2], m_data[idx + 3])) { idx += 4; } else { return false; } } return true; } Result StringView::skipWhitespace() { using RetType = Result; if (m_data == nullptr) { return RetType::error(FudStatus::NullPointer); } size_t index = 0; while (m_length > 0 && charIsSpace(static_cast(m_data[0]))) { m_data++; m_length--; index++; } return RetType::okay(index); } Result StringView::trimWhitespace() { using RetType = Result; if (m_data == nullptr) { return RetType::error(FudStatus::NullPointer); } size_t count = 0; while (m_length > 0 && charIsSpace(static_cast(m_data[m_length - 1]))) { m_length--; count++; } return RetType::okay(count); } bool StringView::advance() { if (m_length < 1) { return false; } m_length--; m_data++; return true; } void StringView::advanceUnsafe() { fudAssert(m_length > 0); m_length--; m_data++; } bool StringView::advance(size_t size) { if (size > m_length) { return false; } m_length -= size; m_data += size; return true; } void StringView::advanceUnsafe(size_t size) { fudAssert(size <= m_length); m_length -= size; m_data += size; } #if 0 FudStatus fud_string_truncate(ExtBasicString* source, ssize_t newLength) { if (source == nullptr) { return FudStatus::NullPointer; } StringBorrow wrapper{*source}; if (!wrapper.valid()) { return FudStatus::StringInvalid; } if ((newLength > 0 && static_cast(newLength) > source->m_length) || (static_cast(-newLength) > source->m_length)) { return FudStatus::InvalidInput; } if (newLength < 0) { source->m_length = source->m_length - static_cast(-newLength); } else { source->m_length = static_cast(newLength); } return wrapper.nullTerminate(); } FudStatus fud_string_reverse(ExtBasicString* source) { if (source == nullptr || source->m_data == nullptr) { return FudStatus::NullPointer; } return fud_string_reverse_substring(source, StringView{source->m_length, source->m_data}); } FudStatus fud_string_reverse_substring(ExtBasicString* source, StringView subString) { auto dataOffset = subString.data - source->m_data; if (dataOffset < 0 || static_cast(dataOffset) > source->m_length) { return FudStatus::InvalidInput; } if (static_cast(dataOffset) + subString.length > source->m_length) { return FudStatus::InvalidInput; } if (source == nullptr || source->m_data == nullptr) { return FudStatus::NullPointer; } StringView view{subString}; size_t index = 0; auto* data = source->m_data + dataOffset; while (index < subString.length) { if (fud_char_is_ascii(static_cast(data[index]))) { index++; continue; } auto utf8 = FudUtf8::fromStringView(view, index); if (!utf8.valid()) { return ExtUtf8Invalid; } const auto* utf8Data = utf8.data(); if (utf8Data == nullptr) { return ExtFailure; } auto utf8Size = utf8.size(); switch (utf8Size) { case 2: data[index] = utf8Data[1]; data[index + 1] = utf8Data[0]; break; case 3: data[index] = utf8Data[2]; data[index + 2] = utf8Data[0]; break; case 4: data[index] = utf8Data[3]; data[index + 1] = utf8Data[2]; data[index + 2] = utf8Data[1]; data[index + 3] = utf8Data[0]; break; default: return ExtFailure; } index += utf8Size; } DataView dataView{subString.length, data}; reverse(dataView); return FudStatus::Success; } FudStatus fud_string_compare(StringView levo, StringView dextro, int* difference) { if (anyAreNull(difference, levo.data, dextro.data)) { return FudStatus::NullPointer; } int diff = 0; size_t index = 0; while (diff == 0 && index < levo.length && index < dextro.length) { diff = levo.data[index] - dextro.data[index]; index++; } if (diff != 0 || levo.length == dextro.length) { /* nothing to do */ } else if (levo.length > dextro.length) { diff = static_cast(levo.data[index]); } else { diff = -static_cast(dextro.data[index]); } *difference = diff; return FudStatus::Success; } FudStatus fud_string_chr(StringView extStringView, char character, size_t* index) { if (anyAreNull(extStringView.data, index)) { return FudStatus::NullPointer; } bool found = false; for (size_t localIndex = 0; localIndex < extStringView.length; ++localIndex) { if (extStringView.data[localIndex] == static_cast(character)) { *index = localIndex; found = true; break; } } if (found) { return FudStatus::Success; } return ExtNotFound; } FudStatus fud_string_unicode_chr(StringView extString, const ExtUtf8* unicode, size_t* index) { if (anyAreNull(extString.data, unicode, index)) { return FudStatus::NullPointer; } if (!unicode->valid()) { return ExtUtf8Invalid; } size_t charSize = unicode->size(); ExtDebugAssert(charSize != 0); const uint8_t* dataMem = unicode->data(); ExtDebugAssert(dataMem != nullptr); Array localData{}; auto copyStatus = ExtCopyMem(localData.data(), localData.size(), dataMem, charSize); ExtDebugAssert(copyStatus == FudStatus::Success); for (size_t sIdx = 0; sIdx + charSize - 1 < extString.length;) { auto localChar = FudUtf8::fromStringView(extString, sIdx); if (!localChar.valid()) { return ExtUtf8Invalid; } if (localChar.m_variant == unicode->m_variant) { *index = sIdx; return FudStatus::Success; } sIdx += localChar.size(); } return ExtNotFound; } FudStatus fud_string_span_c_api( const StringView& inputView, const StringView& characterSetString, StringView& result, bool inSet) { size_t firstIndex = inputView.length; size_t sIdx = 0; while (sIdx < firstIndex) { auto stringChar = FudUtf8::fromStringView(inputView, sIdx); if (!stringChar.valid()) { return ExtUtf8Invalid; } size_t cIdx = 0; bool found = false; while (firstIndex > 0 && cIdx < firstIndex && cIdx < characterSetString.length) { auto setChar = FudUtf8::fromStringView(characterSetString, cIdx); if (!setChar.valid()) { return ExtUtf8Invalid; } if (stringChar == setChar) { found = true; } cIdx += setChar.size(); } if (!inSet && found) { firstIndex = sIdx; } else if (inSet && !found) { if (sIdx > 0) { firstIndex = sIdx; } break; } sIdx += stringChar.size(); } if (firstIndex < inputView.length) { result.length = inputView.length - firstIndex; result.data = inputView.data + firstIndex; return FudStatus::Success; } return ExtNotFound; } FudStatus fud_string_span_set(StringView inputView, const ExtUtf8Set* characterSet, StringView* stringView, bool inSet) { if (anyAreNull(inputView.data, characterSet, stringView)) { return FudStatus::NullPointer; } if (!characterSet->valid()) { return ExtUtf8Invalid; } size_t firstIndex = inputView.length; size_t sIdx = 0; while (sIdx < firstIndex) { auto localChar = FudUtf8::fromStringView(inputView, sIdx); if (!localChar.valid()) { return ExtUtf8Invalid; } bool found = characterSet->contains(localChar); if (!inSet && found) { firstIndex = sIdx; } else if (inSet && !found) { if (sIdx > 0) { firstIndex = sIdx; } break; } sIdx += localChar.size(); } if (firstIndex < inputView.length) { stringView->length = inputView.length - firstIndex; stringView->data = inputView.data + firstIndex; return FudStatus::Success; } return ExtNotFound; } FudStatus fud_string_span(StringView extString, StringView characterSetString, StringView* result) { if (result == nullptr) { return FudStatus::NullPointer; } const StringView inputView{extString}; const StringView characterSet{characterSetString}; return fud_string_span_c_api(inputView, characterSet, *result, true); } FudStatus fud_string_c_span(StringView extString, StringView characterSetString, StringView* result) { if (result == nullptr) { return FudStatus::NullPointer; } const StringView inputView{extString}; const StringView characterSet{characterSetString}; return fud_string_span_c_api(inputView, characterSet, *result, false); } FudStatus fud_string_span_set(StringView extString, const ExtUtf8Set* characterSet, StringView* stringView) { return fud_string_span_set(extString, characterSet, stringView, true); } FudStatus fud_string_c_span_set(StringView extString, const ExtUtf8Set* characterSet, StringView* stringView) { return fud_string_span_set(extString, characterSet, stringView, false); } FudStatus fud_string_find_substring(StringView haystack, StringView needle, StringView* stringView) { if (anyAreNull(haystack.data, needle.data, stringView)) { return FudStatus::NullPointer; } if (needle.length > haystack.length) { return ExtNotFound; } if (needle.length == 1) { size_t index = 0; auto chrFindStatus = fud_string_chr(haystack, static_cast(needle.data[0]), &index); if (chrFindStatus == FudStatus::Success) { stringView->data = haystack.data + index; stringView->length = 1; } return chrFindStatus; } size_t haystackIdx = 0; while (haystackIdx < haystack.length - needle.length) { StringView lhs; lhs.data = haystack.data + haystackIdx; lhs.length = haystack.length - haystackIdx; size_t lhsIndex = 0; auto chrFindStatus = fud_string_chr(lhs, static_cast(needle.data[0]), &lhsIndex); if (chrFindStatus != FudStatus::Success) { return chrFindStatus; } haystackIdx += lhsIndex; // GE or GT? if (haystackIdx + needle.length >= haystack.length) { break; } lhs.data = haystack.data + haystackIdx; lhs.length = needle.length; int difference = -1; auto cmpStatus = fud_string_compare(lhs, needle, &difference); ExtDebugAssert(cmpStatus == FudStatus::Success); if (difference == 0) { stringView->data = lhs.data; stringView->length = lhs.length; return FudStatus::Success; } haystackIdx++; } 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 ExtRangeError; } 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