summaryrefslogtreecommitdiff
path: root/source/fud_string_view.cpp
diff options
context:
space:
mode:
authorDominick Allen <djallen@librehumanitas.org>2024-10-02 11:42:00 -0500
committerDominick Allen <djallen@librehumanitas.org>2024-10-02 11:42:00 -0500
commit7eea7cd5e5b451de9db5bd289f8b5d152d5803f5 (patch)
treed4c948fc00f7c3c63fd9c6c8d85ac1c57c8241b5 /source/fud_string_view.cpp
parent132ddf65fe6e253ebd113036d61c51cd0eb5e827 (diff)
Separate string view, implement string appending semantics.
Diffstat (limited to 'source/fud_string_view.cpp')
-rw-r--r--source/fud_string_view.cpp1131
1 files changed, 1131 insertions, 0 deletions
diff --git a/source/fud_string_view.cpp b/source/fud_string_view.cpp
new file mode 100644
index 0000000..4795cf2
--- /dev/null
+++ b/source/fud_string_view.cpp
@@ -0,0 +1,1131 @@
+/*
+ * 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<size_t, FudStatus> StringView::skipWhitespace()
+{
+ using RetType = Result<size_t, FudStatus>;
+ if (m_data == nullptr) {
+ return RetType::error(FudStatus::NullPointer);
+ }
+ size_t index = 0;
+ while (m_length > 0 && char_is_space(static_cast<char>(m_data[0]))) {
+ m_data++;
+ m_length--;
+ index++;
+ }
+
+ return RetType::okay(index);
+}
+
+Result<size_t, FudStatus> StringView::trimWhitespace()
+{
+ using RetType = Result<size_t, FudStatus>;
+ if (m_data == nullptr) {
+ return RetType::error(FudStatus::NullPointer);
+ }
+
+ size_t count = 0;
+ while (m_length > 0 && char_is_space(static_cast<char>(m_data[m_length - 1]))) {
+ m_length--;
+ count++;
+ }
+
+ return RetType::okay(count);
+}
+
+#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<size_t>(newLength) > source->m_length) ||
+ (static_cast<size_t>(-newLength) > source->m_length)) {
+ return FudStatus::InvalidInput;
+ }
+
+ if (newLength < 0) {
+ source->m_length = source->m_length - static_cast<size_t>(-newLength);
+ } else {
+ source->m_length = static_cast<size_t>(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<size_t>(dataOffset) > source->m_length) {
+ return FudStatus::InvalidInput;
+ }
+ if (static_cast<size_t>(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 (ext_lib_char_is_ascii(static_cast<char>(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;
+ }
+
+ ext_lib::DataView<uint8_t> 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<int>(levo.data[index]);
+ } else {
+ diff = -static_cast<int>(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<uint8_t>(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);
+
+ std::array<uint8_t, 4> 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;
+}
+
+namespace ext_lib {
+
+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;
+}
+
+} // namespace ext_lib
+
+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 ext_lib::fud_string_span_set(extString, characterSet, stringView, true);
+}
+
+FudStatus fud_string_c_span_set(StringView extString, const ExtUtf8Set* characterSet, StringView* stringView)
+{
+ return ext_lib::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<char>(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<char>(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;
+}
+
+namespace ext_lib {
+
+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;
+}
+
+} // namespace ext_lib
+
+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 ext_lib::Array<int8_t, 256> 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<size_t>('0')] == 0);
+static_assert(AsciiLookup[static_cast<size_t>('9')] == 9);
+static_assert(AsciiLookup[static_cast<size_t>('a')] == 10);
+static_assert(AsciiLookup[static_cast<size_t>('A')] == 10);
+static_assert(AsciiLookup[static_cast<size_t>('f')] == 15);
+static_assert(AsciiLookup[static_cast<size_t>('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 <typename T>
+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 = ext_lib::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<uint8_t>(digitResult);
+ if (std::numeric_limits<T>::max() / radix < num) {
+ return FudStatus::InvalidInput;
+ }
+ num *= radix;
+ if (std::numeric_limits<T>::max() - digit < num) {
+ return FudStatus::InvalidInput;
+ }
+ num += digit;
+ digitIndex++;
+ }
+ if (digitIndex < 1) {
+ return FudStatus::InvalidInput;
+ }
+
+ index = skipIndex + digitIndex;
+ number = num;
+
+ return FudStatus::Success;
+}
+
+template <typename T>
+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 <typename T>
+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<uint8_t>(digitResult);
+ if (std::numeric_limits<T>::max() / radix < num) {
+ return FudStatus::InvalidInput;
+ }
+ num = static_cast<T>(num * radix);
+ if (std::numeric_limits<T>::max() - digit < num) {
+ return FudStatus::InvalidInput;
+ }
+ num = static_cast<T>(num + digit);
+ digitIndex++;
+ }
+
+ return FudStatus::Success;
+}
+
+template <typename T>
+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<uint8_t>(digitResult);
+ if ((std::numeric_limits<T>::min() / radix > num)) {
+ return FudStatus::InvalidInput;
+ }
+ num = static_cast<T>(num * radix);
+ if (std::numeric_limits<T>::min() + digit > num) {
+ return FudStatus::InvalidInput;
+ }
+ num = static_cast<T>(num - digit);
+ digitIndex++;
+ }
+
+ return FudStatus::Success;
+}
+
+template <typename T>
+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 = ext_lib::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 <typename T>
+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 <typename T>
+bool isNanOrInf(T& num, StringView& view, T& sign, size_t& digitIndex)
+{
+ if (view.length >= 3) {
+ std::array<uint8_t, 3> letters{{view.data[0], view.data[1], view.data[2]}};
+ ext_lib::mapMut(letters, ext_lib_char_to_lower);
+ if (letters[0] == 'i' && letters[1] == 'n' && letters[2] == 'f') {
+ num = sign * std::numeric_limits<T>::infinity();
+ digitIndex = 3;
+ return true;
+ }
+ if (letters[0] == 'n' && letters[1] == 'a' && letters[2] == 'n') {
+ num = std::numeric_limits<T>::quiet_NaN();
+ ;
+ digitIndex = 3;
+ return true;
+ }
+ }
+ return false;
+}
+
+template <typename T>
+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<T>(digitResult) * sign;
+ num *= static_cast<T>(radix);
+
+ num += digit;
+ digitIndex++;
+ }
+ return FudStatus::Success;
+}
+
+template <typename T>
+FudStatus getExponent(const 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<T>(radix), static_cast<T>(exponent));
+ return FudStatus::Success;
+}
+
+template <typename T>
+FudStatus getFraction(const StringView view, size_t& digitIndex, T& num, T sign, uint8_t radix, bool& foundExponent)
+{
+ auto radixDiv = 1.0F / static_cast<T>(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<T>(digitResult) * sign;
+ num += digit * radixDiv;
+ radixDiv /= static_cast<T>(radix);
+ digitIndex++;
+ }
+ return FudStatus::Success;
+}
+
+template <typename T>
+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 <typename T>
+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