summaryrefslogtreecommitdiff
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
parent132ddf65fe6e253ebd113036d61c51cd0eb5e827 (diff)
Separate string view, implement string appending semantics.
-rw-r--r--CMakeLists.txt2
-rw-r--r--include/fud_memory.hpp2
-rw-r--r--include/fud_string.hpp94
-rw-r--r--include/fud_string_view.hpp101
-rw-r--r--include/fud_utf8_iterator.hpp2
-rw-r--r--source/fud_string.cpp1231
-rw-r--r--source/fud_string_view.cpp1131
7 files changed, 1366 insertions, 1197 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index d17a027..1f14216 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -20,6 +20,7 @@ add_library(fud SHARED
source/fud_assert.cpp
source/fud_memory.cpp
source/fud_c_file.cpp
+ source/fud_string_view.cpp
source/fud_string.cpp
source/fud_utf8.cpp
source/fud_utf8_iterator.cpp
@@ -93,6 +94,7 @@ set(FUD_HEADERS
"include/fud_result.hpp"
"include/fud_status.hpp"
"include/fud_string.hpp"
+ "include/fud_string_view.hpp"
"include/fud_unique_array.hpp"
"include/fud_utf8.hpp"
"include/fud_utf8_iterator.hpp"
diff --git a/include/fud_memory.hpp b/include/fud_memory.hpp
index d6708cd..41393bd 100644
--- a/include/fud_memory.hpp
+++ b/include/fud_memory.hpp
@@ -28,7 +28,7 @@
namespace fud {
extern void* fudAlloc(size_t size);
-extern void* fudRealloc(size_t size);
+extern void* fudRealloc(void* ptr, size_t size);
extern void fudFree(void* ptr);
// An allocating function which returns null on failure.
diff --git a/include/fud_string.hpp b/include/fud_string.hpp
index cd8e8f1..1a38d43 100644
--- a/include/fud_string.hpp
+++ b/include/fud_string.hpp
@@ -18,6 +18,8 @@
#ifndef FUD_STRING_HPP
#define FUD_STRING_HPP
+#include "fud_status.hpp"
+#include "fud_string_view.hpp"
#include "fud_utf8.hpp"
#include <climits>
@@ -92,7 +94,9 @@ class String {
[[nodiscard]] bool utf8Valid() const;
- [[nodiscard]] FudStatus nullTerminate();
+ FudStatus nullTerminate();
+
+ FudStatus reserve(size_t newCapacity);
[[nodiscard]] std::optional<utf8> back();
@@ -105,15 +109,23 @@ class String {
return m_capacity - 1U - m_length;
}
- [[nodiscard]] FudStatus pushBack(char letter);
+ [[nodiscard]] inline StringView asView() const {
+ return StringView(*this);
+ }
+
+ FudStatus pushBack(char letter);
- [[nodiscard]] FudStatus pushBack(utf8 letter);
+ FudStatus pushBack(utf8 letter);
- [[nodiscard]] FudStatus pushBack(const FudUtf8& letter);
+ FudStatus pushBack(const FudUtf8& letter);
std::optional<utf8> pop();
- [[nodiscard]] FudStatus append(StringView source);
+ FudStatus append(const char* source);
+
+ FudStatus append(const String& source);
+
+ FudStatus append(StringView source);
[[nodiscard]] String catenate(const String& rhs) const;
@@ -127,6 +139,7 @@ class String {
private:
void cleanup();
+ FudStatus resize(size_t newCapacity);
using BufType = Array<utf8, SSO_BUF_SIZE>;
union {
@@ -142,77 +155,6 @@ class String {
}
};
-class StringView {
- public:
- constexpr StringView() noexcept : m_length(0), m_data{nullptr}
- {
- }
-
- constexpr StringView(size_t strLen, const utf8* strData) : m_length(strLen), m_data{strData}
- {
- }
-
- StringView(size_t strLen, const char* strData) :
- m_length(strLen), // line break
- m_data{reinterpret_cast<const utf8*>(strData)} // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
- {
- }
-
- explicit constexpr StringView(const StringView& view) noexcept = default;
-
- explicit constexpr StringView(const String& fudString) noexcept : StringView(fudString.length(), fudString.data())
- {
- }
-
- [[nodiscard]] constexpr size_t length() const
- {
- return m_length;
- }
-
- [[nodiscard]] constexpr const utf8* data() const
- {
- return m_data;
- }
-
- [[nodiscard]] bool nullTerminated() const;
-
- [[nodiscard]] bool utf8Valid() const;
-
- Result<size_t, FudStatus> skipWhitespace();
-
- Result<size_t, FudStatus> trimWhitespace();
-
- FudStatus toUint8(uint8_t& number, uint8_t specifiedRadix, size_t& strLen) const;
-
- FudStatus toUint16(uint16_t& number, uint8_t specifiedRadix, size_t& strLen) const;
-
- FudStatus toUint32(uint32_t& number, uint8_t specifiedRadix, size_t& strLen) const;
-
- FudStatus toUint64(uint64_t& number, uint8_t specifiedRadix, size_t& strLen) const;
-
- FudStatus toInt8(int8_t& number, uint8_t specifiedRadix, size_t& strLen) const;
-
- FudStatus toInt16(int16_t& number, uint8_t specifiedRadix, size_t& strLen) const;
-
- FudStatus toInt32(int32_t& number, uint8_t specifiedRadix, size_t& strLen) const;
-
- FudStatus toInt64(int64_t& number, uint8_t specifiedRadix, size_t& strLen) const;
-
- FudStatus toFloat(float& number, size_t& strLen) const;
-
- FudStatus toDouble(double& number, size_t& strLen) const;
-
- private:
- size_t m_length;
- const utf8* m_data;
-};
-
-FudStatus skipWhitespace(StringView& view, size_t& skipIndex);
-
-ssize_t cStringLength(const char* str);
-
-ssize_t cStringLength(const char* str, size_t maxLength);
-
} // namespace fud
#endif
diff --git a/include/fud_string_view.hpp b/include/fud_string_view.hpp
new file mode 100644
index 0000000..4796003
--- /dev/null
+++ b/include/fud_string_view.hpp
@@ -0,0 +1,101 @@
+/*
+ * 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.
+ */
+
+
+#ifndef FUD_STRING_VIEW_HPP
+#define FUD_STRING_VIEW_HPP
+
+#include "fud_status.hpp"
+#include "fud_utf8.hpp"
+
+namespace fud {
+
+class String;
+
+class StringView {
+ public:
+ constexpr StringView() noexcept = default;
+ constexpr StringView(const StringView& rhs) noexcept = default;
+ constexpr StringView(StringView&& rhs) noexcept = default;
+ constexpr ~StringView() noexcept = default;
+ constexpr StringView& operator=(const StringView& rhs) = default;
+ constexpr StringView& operator=(StringView&& rhs) = default;
+
+ constexpr StringView(size_t strLen, const utf8* strData) : m_length(strLen), m_data{strData}
+ {
+ }
+
+ StringView(size_t strLen, const char* strData) :
+ m_length(strLen), // line break
+ m_data{reinterpret_cast<const utf8*>(strData)} // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
+ {
+ }
+
+ explicit StringView(const String& fudString) noexcept;
+
+ [[nodiscard]] constexpr size_t length() const
+ {
+ return m_length;
+ }
+
+ [[nodiscard]] constexpr const utf8* data() const
+ {
+ return m_data;
+ }
+
+ [[nodiscard]] bool nullTerminated() const;
+
+ [[nodiscard]] bool utf8Valid() const;
+
+ Result<size_t, FudStatus> skipWhitespace();
+
+ Result<size_t, FudStatus> trimWhitespace();
+
+ FudStatus toUint8(uint8_t& number, uint8_t specifiedRadix, size_t& strLen) const;
+
+ FudStatus toUint16(uint16_t& number, uint8_t specifiedRadix, size_t& strLen) const;
+
+ FudStatus toUint32(uint32_t& number, uint8_t specifiedRadix, size_t& strLen) const;
+
+ FudStatus toUint64(uint64_t& number, uint8_t specifiedRadix, size_t& strLen) const;
+
+ FudStatus toInt8(int8_t& number, uint8_t specifiedRadix, size_t& strLen) const;
+
+ FudStatus toInt16(int16_t& number, uint8_t specifiedRadix, size_t& strLen) const;
+
+ FudStatus toInt32(int32_t& number, uint8_t specifiedRadix, size_t& strLen) const;
+
+ FudStatus toInt64(int64_t& number, uint8_t specifiedRadix, size_t& strLen) const;
+
+ FudStatus toFloat(float& number, size_t& strLen) const;
+
+ FudStatus toDouble(double& number, size_t& strLen) const;
+
+ private:
+ size_t m_length{0};
+ const utf8* m_data{nullptr};
+};
+
+FudStatus skipWhitespace(StringView& view, size_t& skipIndex);
+
+ssize_t cStringLength(const char* str);
+
+ssize_t cStringLength(const char* str, size_t maxLength);
+
+} // namespace fud
+
+#endif
diff --git a/include/fud_utf8_iterator.hpp b/include/fud_utf8_iterator.hpp
index 3721b00..ec35927 100644
--- a/include/fud_utf8_iterator.hpp
+++ b/include/fud_utf8_iterator.hpp
@@ -34,7 +34,7 @@ class Utf8Iterator {
// NOLINTEND(cppcoreguidelines-avoid-const-or-ref-data-members)
public:
- explicit constexpr Utf8Iterator(const String& fudString) : m_view{fudString}
+ explicit Utf8Iterator(const String& fudString) : m_view{fudString}
{
}
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
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