summaryrefslogtreecommitdiff
path: root/source/fud_string.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'source/fud_string.cpp')
-rw-r--r--source/fud_string.cpp1231
1 files changed, 112 insertions, 1119 deletions
diff --git a/source/fud_string.cpp b/source/fud_string.cpp
index b10f6ee..c1937be 100644
--- a/source/fud_string.cpp
+++ b/source/fud_string.cpp
@@ -84,6 +84,9 @@ String::String(const String& rhs) : m_length{rhs.m_length}, m_capacity{rhs.m_cap
String::String(String&& rhs) : m_length{rhs.m_length}, m_capacity{rhs.m_capacity}
{
+ if (isLarge()) {
+ cleanup();
+ }
if (rhs.isLarge()) {
m_data = rhs.m_data;
rhs.m_data = nullptr;
@@ -143,6 +146,37 @@ void String::cleanup()
}
}
+FudStatus String::resize(size_t newCapacity)
+{
+ if (m_length >= newCapacity) {
+ return FudStatus::OperationInvalid;
+ }
+
+ if (!isLarge() && newCapacity <= SSO_BUF_SIZE) {
+ m_capacity = SSO_BUF_SIZE;
+ return FudStatus::Success;
+ }
+
+ if (newCapacity <= SSO_BUF_SIZE) {
+ BufType temp{BufType::constFill(0)};
+ static_cast<void>(copyMem(data(), temp.size(), temp.data(), length()));
+ m_capacity = SSO_BUF_SIZE;
+ fudFree(m_data);
+ m_data = nullptr;
+ copyMem(m_buffer, temp);
+
+ return FudStatus::Success;
+ }
+
+ auto* newData = fudRealloc(m_data, newCapacity);
+ if (newData == nullptr) {
+ return FudStatus::AllocFailure;
+ }
+ m_capacity = newCapacity;
+ m_data = static_cast<utf8*>(newData);
+ return FudStatus::Success;
+}
+
bool String::nullTerminated() const
{
return data() != nullptr && m_length < m_capacity && data()[m_length] == '\0';
@@ -172,6 +206,19 @@ FudStatus String::nullTerminate()
return FudStatus::StringInvalid;
}
+FudStatus String::reserve(size_t newCapacity)
+{
+ if (!valid()) {
+ return FudStatus::StringInvalid;
+ }
+
+ if (newCapacity < m_capacity) {
+ return FudStatus::Success;
+ }
+
+ return resize(newCapacity);
+}
+
[[nodiscard]] std::optional<utf8> String::back()
{
if (!valid()) {
@@ -188,18 +235,23 @@ FudStatus String::nullTerminate()
std::optional<utf8> String::pop()
{
+ if (!valid()) {
+ return std::nullopt;
+ }
+
if (m_length < 1) {
return std::nullopt;
}
+
m_length--;
- auto letter = m_data[m_length];
- m_data[m_length] = '\0';
+ auto letter = data()[m_length];
+ data()[m_length] = '\0';
return letter;
}
FudStatus String::pushBack(char letter)
{
- return pushBack(static_cast<uint8_t>(letter));
+ return pushBack(static_cast<utf8>(letter));
}
FudStatus String::pushBack(utf8 letter)
@@ -209,12 +261,17 @@ FudStatus String::pushBack(utf8 letter)
}
if (remainingLength() < 1) {
- return FudStatus::OperationInvalid;
+ auto newCapacity = m_capacity < SIZE_MAX / 2 ? m_capacity * 2 : SIZE_MAX;
+ const auto resizeStatus = resize(newCapacity);
+ if (resizeStatus != FudStatus::Success) {
+ return resizeStatus;
+ }
+ fudAssert(m_capacity == newCapacity);
}
- m_data[m_length] = letter;
+ data()[m_length] = letter;
m_length++;
- m_data[m_length] = '\0';
+ data()[m_length] = '\0';
return FudStatus::Success;
}
@@ -235,8 +292,16 @@ FudStatus String::pushBack(const FudUtf8& letter)
}
auto letterSize = letter.size();
- if (letterSize > remainingLength()) {
- return FudStatus::OperationInvalid;
+ if (remainingLength() < letterSize) {
+ auto newCapacity = m_capacity < SIZE_MAX / 2 ? m_capacity * 2 : SIZE_MAX;
+ if ((newCapacity - m_capacity) < (letterSize + 1)) {
+ return FudStatus::OperationInvalid;
+ }
+ const auto resizeStatus = resize(newCapacity);
+ if (resizeStatus != FudStatus::Success) {
+ return resizeStatus;
+ }
+ fudAssert(m_capacity == newCapacity);
}
auto copyStatus = copyMem(m_data + m_length, remainingLength(), letterData, letterSize);
@@ -246,11 +311,26 @@ FudStatus String::pushBack(const FudUtf8& letter)
}
m_length += letterSize;
- m_data[m_length] = '\0';
+ data()[m_length] = '\0';
return FudStatus::Success;
}
+FudStatus String::append(const char* source)
+{
+ auto lenResult = cStringLength(source);
+ if (lenResult < 0 || lenResult >= SSIZE_MAX) {
+ return FudStatus::InvalidInput;
+ }
+
+ return this->append(StringView{static_cast<size_t>(lenResult), source});
+}
+
+FudStatus String::append(const String& source)
+{
+ return append(source.asView());
+}
+
FudStatus String::append(StringView source)
{
if (!valid()) {
@@ -261,18 +341,31 @@ FudStatus String::append(StringView source)
return FudStatus::Aliased;
}
- const auto newLength = m_length + source.length();
+ const auto newLength = length() + source.length();
+ if (newLength < length())
+ {
+ return FudStatus::OperationInvalid;
+ }
const auto newSize = newLength + 1;
- if (newSize >= m_capacity) {
+ if (newSize < newLength)
+ {
return FudStatus::OperationInvalid;
}
+ if (newSize >= m_capacity) {
+ auto newCapacity = newSize < SIZE_MAX / 2 ? newSize * 2 : SIZE_MAX;
+ const auto resizeStatus = resize(newCapacity);
+ if (resizeStatus != FudStatus::Success) {
+ return resizeStatus;
+ }
+ fudAssert(m_capacity == newCapacity);
+ }
- auto* destPtr = m_data + m_length;
+ auto* destPtr = data() + length();
auto status = copyMem(destPtr, m_capacity, source.data(), source.length());
- if (status == FudStatus::Success) { // likely
- m_length += source.length();
- status = nullTerminate();
- }
+ fudAssert(status == FudStatus::Success);
+
+ m_length += source.length();
+ status = nullTerminate();
return status;
}
@@ -295,6 +388,9 @@ String String::catenate(const String& rhs) const
output.m_length = m_length + rhs.length();
output.m_capacity = output.m_length + 1;
+ if (output.m_capacity < SSO_BUF_SIZE) {
+ output.m_capacity = SSO_BUF_SIZE;
+ }
if (output.isLarge()) {
output.m_data = static_cast<utf8*>(fudAlloc(output.m_capacity));
}
@@ -342,1107 +438,4 @@ const utf8* String::end() const
return data() + size();
}
-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