summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt61
-rw-r--r--cmake/FudConfig.cmake.in8
-rw-r--r--cmake/fud-config.cmake1
-rw-r--r--cmake/fud.pc.in10
-rw-r--r--cmake/warnings.cmake1
-rw-r--r--include/fud_assert.hpp31
-rw-r--r--include/fud_c_file.hpp3
-rw-r--r--include/fud_status.hpp8
-rw-r--r--include/fud_string.hpp12
-rw-r--r--include/fud_utf8.hpp86
-rw-r--r--include/libfud.hpp20
-rw-r--r--source/fud_assert.cpp26
-rw-r--r--source/fud_c_file.cpp5
-rw-r--r--source/fud_memory.cpp2
-rw-r--r--source/fud_string.cpp148
-rw-r--r--source/fud_utf8.cpp2
-rw-r--r--source/fud_utf8_iterator.cpp14
-rw-r--r--test/CMakeLists.txt7
-rw-r--r--test/test_common.cpp18
-rw-r--r--test/test_common.hpp43
-rw-r--r--test/test_deserialize_number.cpp503
-rw-r--r--test/test_result.cpp2
-rw-r--r--test/test_string.cpp296
23 files changed, 1144 insertions, 163 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index fc500ab..4d3e399 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -13,29 +13,36 @@ set(CXX_CPPCHECK "project=build/compile_commands.json;enable=information;force")
set(CMAKE_EXPORT_COMPILE_COMMANDS true)
-add_library(libfud SHARED
+add_library(fud SHARED
source/libfud.cpp
+ source/fud_assert.cpp
source/fud_memory.cpp
source/fud_c_file.cpp
source/fud_string.cpp
source/fud_utf8.cpp
source/fud_utf8_iterator.cpp
- )
+)
-target_compile_options(libfud PRIVATE ${FUD_WARNINGS})
+include(cmake/warnings.cmake)
+target_compile_options(fud PRIVATE "${FUD_WARNINGS}")
-target_include_directories(libfud PUBLIC include)
+target_include_directories(fud PUBLIC include)
set_target_properties(
- libfud PROPERTIES
+ fud PROPERTIES
CXX_STANDARD 20
C_STANDARD 23
CXX_EXTENSIONS OFF
C_EXTENSIONS OFF
CXX_STANDARD_REQUIRED ON)
+
if (FUD_TEST)
-add_subdirectory(test)
+ add_subdirectory(test)
+ # set(CVG_FLAGS -fsanitize=address -fsanitize=undefined --coverage)
+ set(CVG_FLAGS --coverage)
+ target_compile_options(fud PUBLIC ${CVG_FLAGS})
+ target_link_options(fud PUBLIC ${CVG_FLAGS})
endif ()
if (FUD_DOC)
@@ -48,3 +55,45 @@ if (FUD_DOC)
# CONFIG_FILE "Doxyfile"
)
endif()
+
+include(GNUInstallDirs)
+set(INCLUDE_INSTALL_DIR ${CMAKE_INSTALL_INCLUDEDIR}
+ CACHE PATH "Location of header files" )
+set(SYSCONFIG_INSTALL_DIR ${CMAKE_INSTALL_SYSCONFDIR}
+ CACHE PATH "Location of configuration files" )
+
+include(CMakePackageConfigHelpers)
+configure_package_config_file(cmake/FudConfig.cmake.in
+ ${CMAKE_CURRENT_BINARY_DIR}/FudConfig.cmake
+ INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/Foo
+ PATH_VARS INCLUDE_INSTALL_DIR SYSCONFIG_INSTALL_DIR)
+write_basic_package_version_file(
+ ${CMAKE_CURRENT_BINARY_DIR}/FudConfigVersion.cmake
+ VERSION 1.0.0
+ COMPATIBILITY SameMajorVersion )
+install(FILES ${CMAKE_CURRENT_BINARY_DIR}/FudConfig.cmake
+ ${CMAKE_CURRENT_BINARY_DIR}/FudConfigVersion.cmake
+ DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/Fud )
+
+set(FUD_HEADERS
+ "include/fud_array.hpp"
+ "include/fud_assert.hpp"
+ "include/fud_c_file.hpp"
+ "include/fud_fud_type_traits.hpp"
+ "include/fud_memory.hpp"
+ "include/fud_result.hpp"
+ "include/fud_status.hpp"
+ "include/fud_string.hpp"
+ "include/fud_unique_array.hpp"
+ "include/fud_utf8.hpp"
+ "include/fud_utf8_iterator.hpp"
+ "include/libfud.hpp"
+)
+
+set_target_properties(fud PROPERTIES PUBLIC_HEADER "${FUD_HEADERS}")
+
+install(TARGETS fud
+ EXPORT fud
+ PUBLIC_HEADER
+ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/
+)
diff --git a/cmake/FudConfig.cmake.in b/cmake/FudConfig.cmake.in
new file mode 100644
index 0000000..9d08140
--- /dev/null
+++ b/cmake/FudConfig.cmake.in
@@ -0,0 +1,8 @@
+set(FOO_VERSION x.y.z)
+# ...
+@PACKAGE_INIT@
+# ...
+set_and_check(FUD_INCLUDE_DIR "@PACKAGE_INCLUDE_INSTALL_DIR@")
+set_and_check(FUD_SYSCONFIG_DIR "@PACKAGE_SYSCONFIG_INSTALL_DIR@")
+
+check_required_components(Fud)
diff --git a/cmake/fud-config.cmake b/cmake/fud-config.cmake
new file mode 100644
index 0000000..35b1014
--- /dev/null
+++ b/cmake/fud-config.cmake
@@ -0,0 +1 @@
+include(${CMAKE_CURRENT_LIST_DIR}/fudTargets.cmake)
diff --git a/cmake/fud.pc.in b/cmake/fud.pc.in
new file mode 100644
index 0000000..d7359bc
--- /dev/null
+++ b/cmake/fud.pc.in
@@ -0,0 +1,10 @@
+prefix=${pcfiledir}/../..
+exec_prefix=${prefix}
+includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@
+libdir=${exec_prefix}/@CMAKE_INSTALL_LIBDIR@
+
+Name: @PROJECT_NAME@
+Description: Fear Unknown Doubt
+Version: @PROJECT_VERSION@
+Libs: -L${libdir} -lhouse
+Cflags: -I${includedir}
diff --git a/cmake/warnings.cmake b/cmake/warnings.cmake
index 1bddeb8..03d7a32 100644
--- a/cmake/warnings.cmake
+++ b/cmake/warnings.cmake
@@ -26,7 +26,6 @@ set(FUD_WARNINGS
-Wmissing-field-initializers
-Wimport
-Winit-self
- -Winline
-Wchar-subscripts
-Wcomment
-Wconversion
diff --git a/include/fud_assert.hpp b/include/fud_assert.hpp
new file mode 100644
index 0000000..5b63703
--- /dev/null
+++ b/include/fud_assert.hpp
@@ -0,0 +1,31 @@
+/*
+ * 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_ASSERT_HPP
+#define FUD_ASSERT_HPP
+
+namespace fud {
+
+void assertFail(const char* assertion, const char* file, unsigned int line, const char* function) noexcept(false)
+ __attribute__((__noreturn__));
+
+#define fudAssert(expr) \
+ (static_cast<bool>(expr) ? static_cast<void>(0) : assertFail(#expr, __FILE__, __LINE__, __PRETTY_FUNCTION__))
+
+} // namespace fud
+
+#endif
diff --git a/include/fud_c_file.hpp b/include/fud_c_file.hpp
index f89839d..281d216 100644
--- a/include/fud_c_file.hpp
+++ b/include/fud_c_file.hpp
@@ -86,10 +86,13 @@ class CBinaryFile {
public:
CBinaryFile(const String& filename, CFileMode mode);
CBinaryFile(const String& filename, CFileMode mode, const String& extraFlags);
+ CBinaryFile(const CBinaryFile& rhs) = delete;
~CBinaryFile();
+ CBinaryFile& operator=(const CBinaryFile& rhs) = delete;
FileResult open();
void close();
const FILE* file() const;
+ FILE* file();
private:
String m_filename;
diff --git a/include/fud_status.hpp b/include/fud_status.hpp
index 47b14a4..dd84429 100644
--- a/include/fud_status.hpp
+++ b/include/fud_status.hpp
@@ -83,7 +83,10 @@ static inline const char* FudStatusToString(FudStatus status)
}
}
-static inline bool anyAreNull() { return false; }
+static inline bool anyAreNull()
+{
+ return false;
+}
template <typename T>
bool anyAreNull(const T* pointer)
@@ -94,8 +97,7 @@ bool anyAreNull(const T* pointer)
template <typename T, typename... Ts>
bool anyAreNull(T pointer, Ts... pointers)
{
- if (pointer == nullptr)
- {
+ if (pointer == nullptr) {
return true;
}
return anyAreNull(pointers...);
diff --git a/include/fud_string.hpp b/include/fud_string.hpp
index 42009b4..5229f26 100644
--- a/include/fud_string.hpp
+++ b/include/fud_string.hpp
@@ -54,7 +54,7 @@ class String {
[[nodiscard]] constexpr size_t capacity() const
{
- return m_capacity;
+ return m_capacity - 1;
}
/** \brier The underlying data, guaranteed to have c string representation. */
@@ -87,7 +87,7 @@ class String {
[[nodiscard]] bool utf8Valid() const;
- [[nodiscard]] FudStatus nullTerminate() const;
+ [[nodiscard]] FudStatus nullTerminate();
[[nodiscard]] constexpr size_t remainingLength() const
{
@@ -117,11 +117,11 @@ class String {
utf8* m_data;
};
size_t m_length{0};
- size_t m_capacity{0};
+ size_t m_capacity{SSO_BUF_SIZE};
[[nodiscard]] constexpr bool isLarge() const
{
- return m_capacity > SSO_BUF_LENGTH;
+ return m_capacity > SSO_BUF_SIZE;
}
};
@@ -131,12 +131,12 @@ class StringView {
{
}
- constexpr StringView(size_t strLen, const utf8* strData) : m_length(0), m_data{strData}
+ constexpr StringView(size_t strLen, const utf8* strData) : m_length(strLen), m_data{strData}
{
}
StringView(size_t strLen, const char* strData) :
- m_length(0), // line break
+ m_length(strLen), // line break
m_data{reinterpret_cast<const utf8*>(strData)} // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
{
}
diff --git a/include/fud_utf8.hpp b/include/fud_utf8.hpp
index da1a5fe..99766d4 100644
--- a/include/fud_utf8.hpp
+++ b/include/fud_utf8.hpp
@@ -31,43 +31,44 @@ namespace fud {
using utf8 = unsigned char;
-struct StringView;
+class String;
+class StringView;
-constexpr uint8_t ASCII_MASK = 0x7F;
+constexpr utf8 ASCII_MASK = 0x7F;
-constexpr uint8_t UTF8_MB_PATTERN_MASK = 0xC0;
-constexpr uint8_t UTF8_MB_PATTERN = 0x80;
-constexpr uint8_t UTF8_MB_MASK = static_cast<uint8_t>(~UTF8_MB_PATTERN_MASK);
+constexpr utf8 UTF8_MB_PATTERN_MASK = 0xC0;
+constexpr utf8 UTF8_MB_PATTERN = 0x80;
+constexpr utf8 UTF8_MB_MASK = static_cast<utf8>(~UTF8_MB_PATTERN_MASK);
-constexpr uint8_t UTF8_2B_PATTERN_MASK = 0xE0;
-constexpr uint8_t UTF8_2B_PATTERN = 0xC0;
-constexpr uint8_t UTF8_2B_MASK = static_cast<uint8_t>(~UTF8_2B_PATTERN_MASK);
+constexpr utf8 UTF8_2B_PATTERN_MASK = 0xE0;
+constexpr utf8 UTF8_2B_PATTERN = 0xC0;
+constexpr utf8 UTF8_2B_MASK = static_cast<utf8>(~UTF8_2B_PATTERN_MASK);
-constexpr uint8_t UTF8_3B_PATTERN_MASK = 0xF0;
-constexpr uint8_t UTF8_3B_PATTERN = 0xE0;
-constexpr uint8_t UTF8_3B_MASK = static_cast<uint8_t>(~UTF8_3B_PATTERN_MASK);
+constexpr utf8 UTF8_3B_PATTERN_MASK = 0xF0;
+constexpr utf8 UTF8_3B_PATTERN = 0xE0;
+constexpr utf8 UTF8_3B_MASK = static_cast<utf8>(~UTF8_3B_PATTERN_MASK);
-constexpr uint8_t UTF8_4B_PATTERN_MASK = 0xF8;
-constexpr uint8_t UTF8_4B_PATTERN = 0xF0;
-constexpr uint8_t UTF8_4B_MASK = static_cast<uint8_t>(~UTF8_4B_PATTERN_MASK);
+constexpr utf8 UTF8_4B_PATTERN_MASK = 0xF8;
+constexpr utf8 UTF8_4B_PATTERN = 0xF0;
+constexpr utf8 UTF8_4B_MASK = static_cast<utf8>(~UTF8_4B_PATTERN_MASK);
namespace privateImpl {
-constexpr bool validUtf8MB(uint8_t code) noexcept
+constexpr bool validUtf8MB(utf8 code) noexcept
{
return (code & UTF8_MB_PATTERN_MASK) == UTF8_MB_PATTERN;
}
} // namespace privateImpl
struct Ascii {
- Array<uint8_t, 1> characters;
+ Array<utf8, 1> characters;
constexpr Ascii() noexcept = default;
- constexpr explicit Ascii(uint8_t chr) noexcept : characters{{chr}}
+ constexpr explicit Ascii(utf8 chr) noexcept : characters{{chr}}
{
}
- [[nodiscard]] constexpr uint8_t character() const noexcept
+ [[nodiscard]] constexpr utf8 character() const noexcept
{
return characters[0];
}
@@ -87,9 +88,9 @@ struct Ascii {
return valid(characters[0]);
}
- static constexpr bool valid(uint8_t character) noexcept
+ static constexpr bool valid(utf8 character) noexcept
{
- return static_cast<uint8_t>(character & ~ASCII_MASK) == 0;
+ return static_cast<utf8>(character & ~ASCII_MASK) == 0;
}
auto operator<=>(const Ascii& other) const noexcept = default;
@@ -107,10 +108,10 @@ static_assert(std::is_standard_layout_v<Ascii>);
*/
struct Utf82Byte {
- constexpr Utf82Byte(uint8_t first, uint8_t second) noexcept : characters{{first, second}}
+ constexpr Utf82Byte(utf8 first, utf8 second) noexcept : characters{{first, second}}
{
}
- Array<uint8_t, 2> characters;
+ Array<utf8, 2> characters;
static constexpr size_t size() noexcept
{
return 2;
@@ -121,18 +122,18 @@ struct Utf82Byte {
return valid(first(), second());
}
- static constexpr bool valid(uint8_t first, uint8_t second) noexcept
+ static constexpr bool valid(utf8 first, utf8 second) noexcept
{
using privateImpl::validUtf8MB;
return ((first & UTF8_2B_PATTERN_MASK) == UTF8_2B_PATTERN) && validUtf8MB(second);
}
- [[nodiscard]] constexpr uint8_t first() const noexcept
+ [[nodiscard]] constexpr utf8 first() const noexcept
{
return characters[0];
}
- [[nodiscard]] constexpr uint8_t second() const noexcept
+ [[nodiscard]] constexpr utf8 second() const noexcept
{
return characters[1];
}
@@ -141,11 +142,11 @@ struct Utf82Byte {
};
struct Utf83Byte {
- constexpr Utf83Byte(uint8_t first, uint8_t second, uint8_t third) noexcept : characters{{first, second, third}}
+ constexpr Utf83Byte(utf8 first, utf8 second, utf8 third) noexcept : characters{{first, second, third}}
{
}
- Array<uint8_t, 3> characters;
+ Array<utf8, 3> characters;
static constexpr size_t size() noexcept
{
@@ -157,23 +158,23 @@ struct Utf83Byte {
return valid(first(), second(), third());
}
- static constexpr bool valid(uint8_t first, uint8_t second, uint8_t third) noexcept
+ static constexpr bool valid(utf8 first, utf8 second, utf8 third) noexcept
{
using privateImpl::validUtf8MB;
return ((first & UTF8_3B_PATTERN_MASK) == UTF8_3B_PATTERN) && validUtf8MB(second) && validUtf8MB(third);
}
- [[nodiscard]] constexpr uint8_t first() const noexcept
+ [[nodiscard]] constexpr utf8 first() const noexcept
{
return characters[0];
}
- [[nodiscard]] constexpr uint8_t second() const noexcept
+ [[nodiscard]] constexpr utf8 second() const noexcept
{
return characters[1];
}
- [[nodiscard]] constexpr uint8_t third() const noexcept
+ [[nodiscard]] constexpr utf8 third() const noexcept
{
return characters[2];
}
@@ -182,12 +183,12 @@ struct Utf83Byte {
};
struct Utf84Byte {
- constexpr Utf84Byte(uint8_t first, uint8_t second, uint8_t third, uint8_t fourth) noexcept :
+ constexpr Utf84Byte(utf8 first, utf8 second, utf8 third, utf8 fourth) noexcept :
characters{{first, second, third, fourth}}
{
}
- Array<uint8_t, 4> characters;
+ Array<utf8, 4> characters;
static constexpr size_t size() noexcept
{
@@ -199,7 +200,7 @@ struct Utf84Byte {
return valid(first(), second(), third(), fourth());
}
- static constexpr bool valid(uint8_t first, uint8_t second, uint8_t third, uint8_t fourth) noexcept
+ static constexpr bool valid(utf8 first, utf8 second, utf8 third, utf8 fourth) noexcept
{
using privateImpl::validUtf8MB;
if ((first & UTF8_4B_PATTERN_MASK) != UTF8_4B_PATTERN) {
@@ -208,22 +209,22 @@ struct Utf84Byte {
return validUtf8MB(second) && validUtf8MB(third) && validUtf8MB(fourth);
}
- [[nodiscard]] constexpr uint8_t first() const noexcept
+ [[nodiscard]] constexpr utf8 first() const noexcept
{
return characters[0];
}
- [[nodiscard]] constexpr uint8_t second() const noexcept
+ [[nodiscard]] constexpr utf8 second() const noexcept
{
return characters[1];
}
- [[nodiscard]] constexpr uint8_t third() const noexcept
+ [[nodiscard]] constexpr utf8 third() const noexcept
{
return characters[2];
}
- [[nodiscard]] constexpr uint8_t fourth() const noexcept
+ [[nodiscard]] constexpr utf8 fourth() const noexcept
{
return characters[3];
}
@@ -246,9 +247,6 @@ static_assert(Utf8TypeSet.m_values[1] == static_cast<uint8_t>(Utf8Type::Utf82Byt
static_assert(Utf8TypeSet.m_values[2] == static_cast<uint8_t>(Utf8Type::Utf83Byte));
static_assert(Utf8TypeSet.m_values[3] == static_cast<uint8_t>(Utf8Type::Utf84Byte));
-class String;
-class StringView;
-
struct FudUtf8 {
Utf8Variant m_variant{Utf8Variant{Ascii{}}};
@@ -287,9 +285,9 @@ struct FudUtf8 {
static constexpr FudUtf8 invalidAscii()
{
- FudUtf8 utf8{};
- utf8.m_variant = Ascii{invalidAsciiCode};
- return utf8;
+ FudUtf8 character{};
+ character.m_variant = Ascii{invalidAsciiCode};
+ return character;
}
[[nodiscard]] constexpr Utf8Type getType() const
diff --git a/include/libfud.hpp b/include/libfud.hpp
index be840a4..ceb1b20 100644
--- a/include/libfud.hpp
+++ b/include/libfud.hpp
@@ -18,8 +18,28 @@
#ifndef LIBFUD_HPP
#define LIBFUD_HPP
+#include "fud_status.hpp" // IWYU pragma: export
+
#include "fud_result.hpp" // IWYU pragma: export
+#include "fud_memory.hpp" // IWYU pragma: export
+
+#include "fud_assert.hpp" // IWYU pragma: export
+
+#include "fud_array.hpp" // IWYU pragma: export
+
+#include "fud_c_file.hpp" // IWYU pragma: export
+
+#include "fud_fud_type_traits.hpp" // IWYU pragma: export
+
+#include "fud_string.hpp" // IWYU pragma: export
+
+#include "fud_unique_array.hpp" // IWYU pragma: export
+
+#include "fud_utf8.hpp" // IWYU pragma: export
+
+#include "fud_utf8_iterator.hpp" // IWYU pragma: export
+
namespace fud {
void fud();
diff --git a/source/fud_assert.cpp b/source/fud_assert.cpp
new file mode 100644
index 0000000..f3358df
--- /dev/null
+++ b/source/fud_assert.cpp
@@ -0,0 +1,26 @@
+#include "fud_assert.hpp"
+
+#include "fud_array.hpp"
+
+#include <cstdio>
+#include <stdexcept>
+#include <format>
+
+namespace fud {
+
+void assertFail(const char* assertion, const char* file, unsigned int line, const char* function) noexcept(false)
+{
+ constexpr size_t ASSERT_MSG_SIZE = 1024;
+ Array<char, ASSERT_MSG_SIZE> buffer{};
+ static_cast<void>(std::format_to_n(
+ buffer.data(),
+ buffer.size() - 1U,
+ "{}:{}: {}:Assertion `{}` failed",
+ file,
+ line,
+ function,
+ assertion));
+ throw std::runtime_error(buffer.data());
+}
+
+}
diff --git a/source/fud_c_file.cpp b/source/fud_c_file.cpp
index ff54d8e..24186bf 100644
--- a/source/fud_c_file.cpp
+++ b/source/fud_c_file.cpp
@@ -57,4 +57,9 @@ const FILE* CBinaryFile::file() const
return m_file;
}
+FILE* CBinaryFile::file()
+{
+ return m_file;
+}
+
} // namespace fud
diff --git a/source/fud_memory.cpp b/source/fud_memory.cpp
index fe6dfae..c4a4aa8 100644
--- a/source/fud_memory.cpp
+++ b/source/fud_memory.cpp
@@ -88,7 +88,7 @@ FudStatus setMemory(void* data, size_t dataSize, uint8_t pattern, size_t count)
for (size_t idx = 0; idx < count; ++idx)
{
- static_cast<char*>(data)[idx] = pattern;
+ static_cast<uint8_t*>(data)[idx] = pattern;
}
return FudStatus::Success;
diff --git a/source/fud_string.cpp b/source/fud_string.cpp
index d690aa9..cd35bdb 100644
--- a/source/fud_string.cpp
+++ b/source/fud_string.cpp
@@ -16,8 +16,7 @@
*/
#include "fud_string.hpp"
-
-#include <cassert>
+#include "fud_assert.hpp"
namespace fud {
@@ -56,16 +55,17 @@ String::String(const char* cString)
if (lenResult < 0 || lenResult >= SSIZE_MAX) {
m_length = 1;
m_capacity = 0;
- } else if (lenResult < SSO_BUF_SIZE) {
+ } else if (static_cast<size_t>(lenResult) < SSO_BUF_SIZE) {
m_length = static_cast<size_t>(lenResult);
- assert(copyMem(m_buffer.data(), m_buffer.size(), cString, m_length));
+ fudAssert(copyMem(m_buffer.data(), m_buffer.size(), cString, m_length) == FudStatus::Success);
+ fudAssert(nullTerminate() == FudStatus::Success);
} else {
m_length = static_cast<size_t>(lenResult);
m_capacity = m_length + 1;
m_data = static_cast<utf8*>(fudAlloc(m_capacity));
- assert(m_data != nullptr);
- assert(copyMem(m_buffer.data(), m_capacity, cString, m_length) == FudStatus::Success);
- assert(nullTerminate() == FudStatus::Success);
+ fudAssert(m_data != nullptr);
+ fudAssert(copyMem(m_buffer.data(), m_capacity, cString, m_length) == FudStatus::Success);
+ fudAssert(nullTerminate() == FudStatus::Success);
}
}
@@ -74,10 +74,10 @@ String::String(const String& rhs) : m_length{rhs.m_length}, m_capacity{rhs.m_cap
if (rhs.valid()) {
if (isLarge()) {
m_data = static_cast<utf8*>(fudAlloc(m_capacity));
- assert(m_data != nullptr);
+ fudAssert(m_data != nullptr);
}
- assert(copyMem(data(), m_capacity, rhs.data(), m_length) == FudStatus::Success);
- assert(nullTerminate() == FudStatus::Success);
+ fudAssert(copyMem(data(), m_capacity, rhs.data(), m_length) == FudStatus::Success);
+ fudAssert(nullTerminate() == FudStatus::Success);
}
}
@@ -87,8 +87,8 @@ String::String(String&& rhs) : m_length{rhs.m_length}, m_capacity{rhs.m_capacity
m_data = rhs.m_data;
rhs.m_data = nullptr;
} else {
- assert(copyMem(m_buffer.data(), m_buffer.size(), rhs.m_buffer.data(), m_length));
- assert(nullTerminate() == FudStatus::Success);
+ m_buffer = std::move(rhs.m_buffer);
+ fudAssert(nullTerminate() == FudStatus::Success);
}
}
@@ -107,10 +107,10 @@ String& String::operator=(const String& rhs)
if (rhs.valid()) {
if (isLarge()) {
m_data = static_cast<utf8*>(fudAlloc(m_capacity));
- assert(m_data != nullptr);
+ fudAssert(m_data != nullptr);
}
- assert(copyMem(data(), m_capacity, rhs.data(), m_length) == FudStatus::Success);
- assert(nullTerminate() == FudStatus::Success);
+ fudAssert(copyMem(data(), m_capacity, rhs.data(), m_length) == FudStatus::Success);
+ fudAssert(nullTerminate() == FudStatus::Success);
}
return *this;
}
@@ -122,8 +122,8 @@ String& String::operator=(String&& rhs) {
m_data = rhs.m_data;
rhs.m_data = nullptr;
} else {
- assert(copyMem(m_buffer.data(), m_buffer.size(), rhs.m_buffer.data(), m_length));
- assert(nullTerminate() == FudStatus::Success);
+ m_buffer = std::move(rhs.m_buffer);
+ fudAssert(nullTerminate() == FudStatus::Success);
}
return *this;
}
@@ -148,10 +148,10 @@ bool String::utf8Valid() const
return view.utf8Valid();
}
-FudStatus String::nullTerminate() const
+FudStatus String::nullTerminate()
{
if (m_length < m_capacity) {
- m_data[m_length] = '\0';
+ data()[m_length] = '\0';
return FudStatus::Success;
}
return FudStatus::StringInvalid;
@@ -266,7 +266,9 @@ String String::append(const String& rhs) const
auto* destPtr = output.data();
auto status = copyMem(destPtr, m_capacity, rhs.data(), rhs.length());
- assert(output.nullTerminate() == FudStatus::Success);
+ fudAssert(status == FudStatus::Success);
+ static_cast<void>(status);
+ fudAssert(output.nullTerminate() == FudStatus::Success);
return output;
}
@@ -333,44 +335,8 @@ Result<size_t, FudStatus> StringView::trimWhitespace()
}
#if 0
-FudStatus ext_string_copy(ExtBasicString* destination, const ExtBasicString* source)
-{
- if (anyAreNull(source, destination) || destination->m_data == nullptr) {
- return FudStatus::NullPointer;
- }
-
- /* TODO: ensure that destination and source aren't aliased, over the entire length. */
- if (source == destination || source->m_data == destination->m_data) {
- return ExtAliased;
- }
-
- if (!String{*source}.valid()) {
- return FudStatus::StringInvalid;
- }
-
- if (destination->m_size <= source->m_length) {
- return FudStatus::OperationInvalid;
- }
-
- auto status = ExtCopyMem(destination->m_data, destination->m_size, source->m_data, source->m_length);
- if (status == FudStatus::Success) {
- destination->m_length = source->m_length;
- status = StringBorrow{*destination}.nullTerminate();
- }
-
- return status;
-}
-FudStatus ext_string_catenate(ExtBasicString* destination, StringView source)
-{
- if (destination == nullptr || anyAreNull(destination->m_data, source.data)) {
- return FudStatus::NullPointer;
- }
-
- return StringBorrow{*destination}.catenate(source);
-}
-
-FudStatus ext_string_truncate(ExtBasicString* source, ssize_t newLength)
+FudStatus fud_string_truncate(ExtBasicString* source, ssize_t newLength)
{
if (source == nullptr) {
return FudStatus::NullPointer;
@@ -394,15 +360,15 @@ FudStatus ext_string_truncate(ExtBasicString* source, ssize_t newLength)
return wrapper.nullTerminate();
}
-FudStatus ext_string_reverse(ExtBasicString* source)
+FudStatus fud_string_reverse(ExtBasicString* source)
{
if (source == nullptr || source->m_data == nullptr) {
return FudStatus::NullPointer;
}
- return ext_string_reverse_substring(source, StringView{source->m_length, source->m_data});
+ return fud_string_reverse_substring(source, StringView{source->m_length, source->m_data});
}
-FudStatus ext_string_reverse_substring(ExtBasicString* source, StringView subString)
+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) {
@@ -461,7 +427,7 @@ FudStatus ext_string_reverse_substring(ExtBasicString* source, StringView subStr
return FudStatus::Success;
}
-FudStatus ext_string_compare(StringView levo, StringView dextro, int* difference)
+FudStatus fud_string_compare(StringView levo, StringView dextro, int* difference)
{
if (anyAreNull(difference, levo.data, dextro.data)) {
return FudStatus::NullPointer;
@@ -486,7 +452,7 @@ FudStatus ext_string_compare(StringView levo, StringView dextro, int* difference
return FudStatus::Success;
}
-FudStatus ext_string_chr(StringView extStringView, char character, size_t* index)
+FudStatus fud_string_chr(StringView extStringView, char character, size_t* index)
{
if (anyAreNull(extStringView.data, index)) {
return FudStatus::NullPointer;
@@ -508,7 +474,7 @@ FudStatus ext_string_chr(StringView extStringView, char character, size_t* index
return ExtNotFound;
}
-FudStatus ext_string_unicode_chr(StringView extString, const ExtUtf8* unicode, size_t* index)
+FudStatus fud_string_unicode_chr(StringView extString, const ExtUtf8* unicode, size_t* index)
{
if (anyAreNull(extString.data, unicode, index)) {
return FudStatus::NullPointer;
@@ -548,7 +514,7 @@ FudStatus ext_string_unicode_chr(StringView extString, const ExtUtf8* unicode, s
namespace ext_lib {
-FudStatus ext_string_span_c_api(
+FudStatus fud_string_span_c_api(
const StringView& inputView,
const StringView& characterSetString,
StringView& result,
@@ -599,7 +565,7 @@ FudStatus ext_string_span_c_api(
return ExtNotFound;
}
-FudStatus ext_string_span_set(StringView inputView, const ExtUtf8Set* characterSet, StringView* stringView, bool inSet)
+FudStatus fud_string_span_set(StringView inputView, const ExtUtf8Set* characterSet, StringView* stringView, bool inSet)
{
if (anyAreNull(inputView.data, characterSet, stringView)) {
return FudStatus::NullPointer;
@@ -642,7 +608,7 @@ FudStatus ext_string_span_set(StringView inputView, const ExtUtf8Set* characterS
} // namespace ext_lib
-FudStatus ext_string_span(StringView extString, StringView characterSetString, StringView* result)
+FudStatus fud_string_span(StringView extString, StringView characterSetString, StringView* result)
{
if (result == nullptr) {
return FudStatus::NullPointer;
@@ -651,10 +617,10 @@ FudStatus ext_string_span(StringView extString, StringView characterSetString, S
const StringView inputView{extString};
const StringView characterSet{characterSetString};
- return ext_string_span_c_api(inputView, characterSet, *result, true);
+ return fud_string_span_c_api(inputView, characterSet, *result, true);
}
-FudStatus ext_string_c_span(StringView extString, StringView characterSetString, StringView* result)
+FudStatus fud_string_c_span(StringView extString, StringView characterSetString, StringView* result)
{
if (result == nullptr) {
return FudStatus::NullPointer;
@@ -663,20 +629,20 @@ FudStatus ext_string_c_span(StringView extString, StringView characterSetString,
const StringView inputView{extString};
const StringView characterSet{characterSetString};
- return ext_string_span_c_api(inputView, characterSet, *result, false);
+ return fud_string_span_c_api(inputView, characterSet, *result, false);
}
-FudStatus ext_string_span_set(StringView extString, const ExtUtf8Set* characterSet, StringView* stringView)
+FudStatus fud_string_span_set(StringView extString, const ExtUtf8Set* characterSet, StringView* stringView)
{
- return ext_lib::ext_string_span_set(extString, characterSet, stringView, true);
+ return ext_lib::fud_string_span_set(extString, characterSet, stringView, true);
}
-FudStatus ext_string_c_span_set(StringView extString, const ExtUtf8Set* characterSet, StringView* stringView)
+FudStatus fud_string_c_span_set(StringView extString, const ExtUtf8Set* characterSet, StringView* stringView)
{
- return ext_lib::ext_string_span_set(extString, characterSet, stringView, false);
+ return ext_lib::fud_string_span_set(extString, characterSet, stringView, false);
}
-FudStatus ext_string_find_substring(StringView haystack, StringView needle, StringView* stringView)
+FudStatus fud_string_find_substring(StringView haystack, StringView needle, StringView* stringView)
{
if (anyAreNull(haystack.data, needle.data, stringView)) {
return FudStatus::NullPointer;
@@ -688,7 +654,7 @@ FudStatus ext_string_find_substring(StringView haystack, StringView needle, Stri
if (needle.length == 1) {
size_t index = 0;
- auto chrFindStatus = ext_string_chr(haystack, static_cast<char>(needle.data[0]), &index);
+ 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;
@@ -702,7 +668,7 @@ FudStatus ext_string_find_substring(StringView haystack, StringView needle, Stri
lhs.data = haystack.data + haystackIdx;
lhs.length = haystack.length - haystackIdx;
size_t lhsIndex = 0;
- auto chrFindStatus = ext_string_chr(lhs, static_cast<char>(needle.data[0]), &lhsIndex);
+ auto chrFindStatus = fud_string_chr(lhs, static_cast<char>(needle.data[0]), &lhsIndex);
if (chrFindStatus != FudStatus::Success) {
return chrFindStatus;
}
@@ -715,7 +681,7 @@ FudStatus ext_string_find_substring(StringView haystack, StringView needle, Stri
lhs.length = needle.length;
int difference = -1;
- auto cmpStatus = ext_string_compare(lhs, needle, &difference);
+ auto cmpStatus = fud_string_compare(lhs, needle, &difference);
ExtDebugAssert(cmpStatus == FudStatus::Success);
if (difference == 0) {
stringView->data = lhs.data;
@@ -745,7 +711,7 @@ FudStatus skipWhitespace(StringView& view, size_t& skipIndex)
} // namespace ext_lib
-FudStatus ext_string_view_skip_whitespace(StringView* view)
+FudStatus fud_string_view_skip_whitespace(StringView* view)
{
if (view == nullptr) {
return FudStatus::NullPointer;
@@ -761,7 +727,7 @@ FudStatus ext_string_view_skip_whitespace(StringView* view)
return FudStatus::Success;
}
-FudStatus ext_string_view_trim_whitespace(StringView* view)
+FudStatus fud_string_view_trim_whitespace(StringView* view)
{
if (view == nullptr) {
return FudStatus::NullPointer;
@@ -1086,42 +1052,42 @@ FudStatus stringViewToSignedInteger(StringView input, T* number, uint8_t specifi
} // namespace impl
-FudStatus ext_string_to_uint8(StringView input, uint8_t* number, uint8_t specifiedRadix, size_t* index)
+FudStatus fud_string_to_uint8(StringView input, uint8_t* number, uint8_t specifiedRadix, size_t* index)
{
return impl::stringViewToUnsignedInteger(input, number, specifiedRadix, index);
}
-FudStatus ext_string_to_uint16(StringView input, uint16_t* number, uint8_t specifiedRadix, size_t* 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 ext_string_to_uint32(StringView input, uint32_t* number, uint8_t specifiedRadix, size_t* 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 ext_string_to_uint64(StringView input, uint64_t* number, uint8_t specifiedRadix, size_t* 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 ext_string_to_int8(StringView input, int8_t* number, uint8_t specifiedRadix, size_t* 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 ext_string_to_int16(StringView input, int16_t* number, uint8_t specifiedRadix, size_t* 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 ext_string_to_int32(StringView input, int32_t* number, uint8_t specifiedRadix, size_t* 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 ext_string_to_int64(StringView input, int64_t* number, uint8_t specifiedRadix, size_t* index)
+FudStatus fud_string_to_int64(StringView input, int64_t* number, uint8_t specifiedRadix, size_t* index)
{
return impl::stringViewToSignedInteger(input, number, specifiedRadix, index);
}
@@ -1247,7 +1213,7 @@ FudStatus stringViewToFloat(StringView input, T& number, size_t& index)
StringView view{input};
size_t skipIndex = 0;
- auto status = ext_lib::skipWhitespace(view, skipIndex);
+ auto status = skipWhitespace(view, skipIndex);
if (status != FudStatus::Success) {
return status;
}
@@ -1346,17 +1312,17 @@ FudStatus stringViewToFloat(StringView input, T* number, size_t* index)
} // namespace impl
-FudStatus ext_string_to_float(StringView input, float* number, size_t* index)
+FudStatus fud_string_to_float(StringView input, float* number, size_t* index)
{
return impl::stringViewToFloat(input, number, index);
}
-FudStatus ext_string_to_double(StringView input, double* number, size_t* index)
+FudStatus fud_string_to_double(StringView input, double* number, size_t* index)
{
return impl::stringViewToFloat(input, number, index);
}
-namespace ext_lib {
+namespace fud {
FudStatus StringView::toUint8(uint8_t& number, uint8_t specifiedRadix, size_t& strLen) const
{
diff --git a/source/fud_utf8.cpp b/source/fud_utf8.cpp
index 5dd5099..1c23581 100644
--- a/source/fud_utf8.cpp
+++ b/source/fud_utf8.cpp
@@ -95,7 +95,7 @@ namespace impl {
/* Assumes that predicate is not a null pointer! */
template <typename Predicate>
-inline FudStatus isAsciiPredicate(FudUtf8* character, bool* pred, Predicate&& predicate)
+FudStatus isAsciiPredicate(FudUtf8* character, bool* pred, Predicate&& predicate)
{
if (anyAreNull(character, pred)) {
return FudStatus::NullPointer;
diff --git a/source/fud_utf8_iterator.cpp b/source/fud_utf8_iterator.cpp
index 4476050..2557dc0 100644
--- a/source/fud_utf8_iterator.cpp
+++ b/source/fud_utf8_iterator.cpp
@@ -25,13 +25,13 @@ std::optional<FudUtf8> Utf8Iterator::peek() const
return std::nullopt;
}
- auto utf8 = FudUtf8::fromStringView(m_view, m_index);
+ auto character = FudUtf8::fromStringView(m_view, m_index);
- if (!utf8.valid()) {
+ if (!character.valid()) {
return std::nullopt;
}
- return utf8;
+ return character;
}
std::optional<FudUtf8> Utf8Iterator::next()
@@ -41,15 +41,15 @@ std::optional<FudUtf8> Utf8Iterator::next()
return std::nullopt;
}
- auto utf8 = FudUtf8::fromStringView(m_view, m_index);
+ auto character = FudUtf8::fromStringView(m_view, m_index);
- if (!utf8.valid()) {
+ if (!character.valid()) {
m_index = m_view.length();
return std::nullopt;
}
- m_index += utf8.size();
- return utf8;
+ m_index += character.size();
+ return character;
}
} // namespace fud
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 9bd9e87..dc48e1f 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -33,9 +33,9 @@ function(fud_add_test test_name)
add_executable(${test_name} test_common.cpp ${FUD_ADD_TEST_SOURCES})
- target_include_directories(${test_name} PUBLIC $<TARGET_PROPERTY:libfud>)
+ target_include_directories(${test_name} PUBLIC $<TARGET_PROPERTY:fud>)
- target_link_libraries(${test_name} PUBLIC GTest::gtest_main libfud)
+ target_link_libraries(${test_name} PUBLIC GTest::gtest_main fud)
target_compile_options(${test_name} PRIVATE ${CVG_FLAGS})
target_link_options(${test_name} PRIVATE ${CVG_FLAGS})
@@ -52,6 +52,8 @@ function(fud_add_test test_name)
endfunction()
fud_add_test(test_result SOURCES test_result.cpp)
+fud_add_test(test_string SOURCES test_string.cpp)
+# fud_add_test(test_deserialize_number SOURCES test_deserialize_number.cpp)
# fud_add_test(test_ext_algorithm SOURCES test_algorithm.cpp)
# fud_add_test(test_ext_array SOURCES
# test_ext_array.cpp
@@ -64,3 +66,4 @@ fud_add_test(test_result SOURCES test_result.cpp)
# fud_add_test(test_ext_string_format SOURCES
# test_ext_string_format.cpp)
+# fud_add_test(test_c_file SOURCES test_c_file.cpp)
diff --git a/test/test_common.cpp b/test/test_common.cpp
index a03c8db..fc37566 100644
--- a/test/test_common.cpp
+++ b/test/test_common.cpp
@@ -1,3 +1,21 @@
+/*
+ * 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 "test_common.hpp"
#include "fud_memory.hpp"
#include <cstdlib>
diff --git a/test/test_common.hpp b/test/test_common.hpp
new file mode 100644
index 0000000..fa6cf09
--- /dev/null
+++ b/test/test_common.hpp
@@ -0,0 +1,43 @@
+/*
+ * 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 EXT_TEST_COMMON_HPP
+#define EXT_TEST_COMMON_HPP
+
+#include <cstddef>
+#include <cstdlib>
+
+namespace ext_lib {
+
+// NOLINTBEGIN(cppcoreguidelines-macro-usage)
+#define MULTI_BYTE_LITERAL "test今日素敵はですねƩ®😀z"
+static_assert(sizeof(MULTI_BYTE_LITERAL) == 38); // NOLINT(readability-magic-numbers)
+#define TWO_BYTE "Ʃ"
+static_assert(sizeof(TWO_BYTE) == 2 + 1);
+#define THREE_BYTE "今"
+static_assert(sizeof(THREE_BYTE) == 3 + 1);
+#define FOUR_BYTE "😀"
+static_assert(sizeof(FOUR_BYTE) == 4 + 1);
+
+#define CHQUOTE "why waste time learning, when ignorance is instantaneous?"
+#define CHARACTER_SET "abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+// NOLINTEND(cppcoreguidelines-macro-usage)
+constexpr size_t charSetSize = sizeof(CHARACTER_SET) - 1;
+
+} // namespace ext_lib
+
+#endif
diff --git a/test/test_deserialize_number.cpp b/test/test_deserialize_number.cpp
new file mode 100644
index 0000000..6b3f85c
--- /dev/null
+++ b/test/test_deserialize_number.cpp
@@ -0,0 +1,503 @@
+/*
+ * 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_array.hpp"
+#include "fud_string.hpp"
+
+#include "gtest/gtest.h"
+
+namespace fud {
+
+template <typename T, typename Func>
+auto testStringToIntegerInvalid(Func&& func)
+{
+ Array<uint8_t, sizeof("7 13 42 0 08 010 0x08")> backing{"7 13 42 0 08 010 0x08"};
+ FudString extString{FudString::withBacking(backing)};
+ StringView view{extString};
+ StringView invalidView{extString};
+ T number = 0;
+ ASSERT_EQ(std::forward<Func>(func)(view, nullptr, 0, nullptr), ExtNullPointer);
+
+ invalidView.data = nullptr;
+ ASSERT_EQ(std::forward<Func>(func)(invalidView, &number, 1, nullptr), ExtNullPointer);
+ invalidView.data = view.data;
+
+ invalidView.length = 0;
+ ASSERT_EQ(std::forward<Func>(func)(invalidView, &number, 0, nullptr), ExtInvalidInput);
+ ASSERT_EQ(std::forward<Func>(func)(view, &number, 1, nullptr), ExtInvalidInput);
+ ASSERT_EQ(std::forward<Func>(func)(view, &number, UINT8_MAX, nullptr), ExtInvalidInput);
+}
+
+template <typename T, size_t BackingSize, size_t WordCount, typename Func>
+auto testStringToNumber(
+ Array<uint8_t, BackingSize>& backing,
+ const Array<size_t, WordCount>& wordSizes,
+ const Array<ExtStatus, WordCount>& statuses,
+ const Array<T, WordCount>& expectedValues,
+ Func&& func,
+ uint8_t radix)
+{
+ FudString extString{FudString::withBacking(backing)};
+ StringView view{extString};
+ T number = 0;
+
+ ASSERT_EQ(std::forward<Func>(func)(view, &number, radix, nullptr), statuses[0]);
+ ASSERT_EQ(number, expectedValues[0]);
+
+ size_t index = 0;
+ for (size_t idx = 0; idx < WordCount; ++idx) {
+
+ T tempNumber{};
+ if (std::forward<Func>(func)(view, &tempNumber, radix, &index) != statuses[idx]) {
+ printf(
+ "どうして? \"%s\" %ld %zu %ld\n",
+ view.data,
+ static_cast<int64_t>(expectedValues[idx]),
+ idx,
+ static_cast<int64_t>(tempNumber));
+ if (idx > 0) {
+ printf("Test: \"%s\"\n", view.data - index);
+ }
+ }
+ EXPECT_EQ(std::forward<Func>(func)(view, &number, radix, &index), statuses[idx]);
+ if (statuses[idx] == ExtSuccess) {
+ ASSERT_EQ(number, expectedValues[idx]);
+ ASSERT_EQ(index, wordSizes[idx]);
+ }
+
+ view.data += wordSizes[idx];
+ view.length -= wordSizes[idx];
+ }
+}
+
+TEST(FudString, StringToUint8Invalid)
+{
+ testStringToIntegerInvalid<uint8_t>(ext_string_to_uint8);
+}
+
+TEST(FudString, StringToUint8RadixUnspec)
+{
+ Array<uint8_t, sizeof("7 13 42 0 018 010 0xFF 0xFF0 256")> backing{"7 13 42 0 018 010 0xFF 0xFF0 256"};
+ constexpr Array<size_t, 10> wordSizes{
+ sizeof("7") - 1,
+ sizeof(" 13") - 1,
+ sizeof(" 42") - 1,
+ sizeof(" 0") - 1,
+ sizeof(" 01") - 1,
+ sizeof("8") - 1,
+ sizeof(" 010") - 1,
+ sizeof(" 0xFF") - 1,
+ sizeof(" 0xFF0") - 1,
+ sizeof(" 256") - 1};
+ constexpr Array<uint8_t, 10> expectedValues{7, 13, 42, 0, 1, 8, 8, 0xFF, 0, 0};
+ constexpr Array<ExtStatus, 10> statuses{
+ ExtSuccess,
+ ExtSuccess,
+ ExtSuccess,
+ ExtSuccess,
+ ExtSuccess,
+ ExtSuccess,
+ ExtSuccess,
+ ExtSuccess,
+ ExtInvalidInput,
+ ExtInvalidInput};
+ testStringToNumber(backing, wordSizes, statuses, expectedValues, ext_string_to_uint8, 0);
+}
+
+TEST(FudString, StringToUint8Octal)
+{
+ Array<uint8_t, sizeof("7 13 42 0 018 010 377 0xFF0")> backing{"7 13 42 0 018 010 377 0xFF0"};
+ constexpr Array<uint8_t, 10> expectedValues{7, 8 + 3, 8 * 4 + 2, 0, 1, 0, 8, 255, 0, 0};
+ constexpr Array<size_t, 10> wordSizes{
+ sizeof("7") - 1,
+ sizeof(" 13") - 1,
+ sizeof(" 42") - 1,
+ sizeof(" 0") - 1,
+ sizeof(" 01") - 1,
+ sizeof("8") - 1,
+ sizeof(" 010") - 1,
+ sizeof(" 377") - 1,
+ sizeof(" 0") - 1,
+ sizeof("xFF0") - 1};
+ constexpr Array<ExtStatus, 10> statuses{
+ ExtSuccess,
+ ExtSuccess,
+ ExtSuccess,
+ ExtSuccess,
+ ExtSuccess,
+ ExtInvalidInput,
+ ExtSuccess,
+ ExtSuccess,
+ ExtSuccess,
+ ExtInvalidInput};
+ testStringToNumber(backing, wordSizes, statuses, expectedValues, ext_string_to_uint8, ExtRadixOctal);
+}
+
+TEST(FudString, StringToUint8Hex)
+{
+ Array<uint8_t, sizeof("7 13 0x42 0 018 010 FF 0xFF0")> backing{"7 13 0x42 0 018 010 FF 0xFF0"};
+ constexpr Array<uint8_t, 8> expectedValues{7, 16 + 3, 16 * 4 + 2, 0, 16 + 8, 16, 255, 0};
+ constexpr Array<size_t, 8> wordSizes{
+ sizeof("7") - 1,
+ sizeof(" 13") - 1,
+ sizeof(" 0x42") - 1,
+ sizeof(" 0") - 1,
+ sizeof(" 018") - 1,
+ sizeof(" 010") - 1,
+ sizeof(" FF") - 1,
+ sizeof(" 0xFF0") - 1};
+ constexpr Array<ExtStatus, 8>
+ statuses{ExtSuccess, ExtSuccess, ExtSuccess, ExtSuccess, ExtSuccess, ExtSuccess, ExtSuccess, ExtInvalidInput};
+ testStringToNumber(backing, wordSizes, statuses, expectedValues, ext_string_to_uint8, ExtRadixHexadecimal);
+}
+
+TEST(FudString, StringToUint16Invalid)
+{
+ testStringToIntegerInvalid<uint16_t>(ext_string_to_uint16);
+}
+
+TEST(FudString, StringToUint16RadixUnspec)
+{
+ Array<uint8_t, sizeof("7 65535 0x42 0 010 0xFFFF 0xFFFF0")> backing{
+ "7 65535 0x42 0 010 0xFFFF 0xFFFF0"};
+ constexpr Array<uint16_t, 7> expectedValues{7, 0xFFFF, 0x42, 0, 8, 0xFFFF, 0};
+ constexpr Array<size_t, 7> wordSizes{
+ sizeof("7") - 1,
+ sizeof(" 65535") - 1,
+ sizeof(" 0x42") - 1,
+ sizeof(" 0") - 1,
+ sizeof(" 010") - 1,
+ sizeof(" 0xFFFF") - 1,
+ sizeof(" 0xFFFF0") - 1};
+ constexpr Array<ExtStatus, 7> statuses{
+ ExtSuccess,
+ ExtSuccess,
+ ExtSuccess,
+ ExtSuccess,
+ ExtSuccess,
+ ExtSuccess,
+ ExtInvalidInput};
+ testStringToNumber(backing, wordSizes, statuses, expectedValues, ext_string_to_uint16, 0);
+}
+
+TEST(FudString, StringToUint16Octal)
+{
+ Array<uint8_t, sizeof("7 13 42 0 010 177777 200000")> backing{"7 13 42 0 010 177777 200000"};
+ constexpr Array<uint16_t, 7> expectedValues{7, 8 + 3, 8 * 4 + 2, 0, 8, 0xFFFF, 0};
+ constexpr Array<size_t, 7> wordSizes{
+ sizeof("7") - 1,
+ sizeof(" 13") - 1,
+ sizeof(" 42") - 1,
+ sizeof(" 0") - 1,
+ sizeof(" 010") - 1,
+ sizeof(" 177777") - 1,
+ sizeof(" 200000") - 1};
+ constexpr Array<ExtStatus, 7> statuses{
+ ExtSuccess,
+ ExtSuccess,
+ ExtSuccess,
+ ExtSuccess,
+ ExtSuccess,
+ ExtSuccess,
+ ExtInvalidInput};
+ testStringToNumber(backing, wordSizes, statuses, expectedValues, ext_string_to_uint16, ExtRadixOctal);
+}
+
+TEST(FudString, StringToUint16Hex)
+{
+ Array<uint8_t, sizeof("7 13 0x42 0 018 010 FFFF 0x10000")> backing{"7 13 0x42 0 018 010 FFFF 0x10000"};
+ constexpr Array<uint16_t, 8> expectedValues{7, 16 + 3, 16 * 4 + 2, 0, 16 + 8, 16, 0xFFFF, 0};
+ constexpr Array<size_t, 8> wordSizes{
+ sizeof("7") - 1,
+ sizeof(" 13") - 1,
+ sizeof(" 0x42") - 1,
+ sizeof(" 0") - 1,
+ sizeof(" 018") - 1,
+ sizeof(" 010") - 1,
+ sizeof(" FFFF") - 1,
+ sizeof(" 200000") - 1};
+ constexpr Array<ExtStatus, 8>
+ statuses{ExtSuccess, ExtSuccess, ExtSuccess, ExtSuccess, ExtSuccess, ExtSuccess, ExtSuccess, ExtInvalidInput};
+ testStringToNumber(backing, wordSizes, statuses, expectedValues, ext_string_to_uint16, ExtRadixHexadecimal);
+}
+
+TEST(FudString, StringToUint64Hex)
+{
+ Array<uint8_t, sizeof("7 13 0x7FFFFFFFFFFFFFFF 0 018 010 -8000000000000000 0xFFFFFFFFFFFFFFFF -8000000000000001")>
+ backing{"7 13 0x7FFFFFFFFFFFFFFF 0 018 010 -8000000000000000 0xFFFFFFFFFFFFFFFF -8000000000000001"};
+ constexpr Array<uint64_t, 9> expectedValues{
+ 7,
+ 16 + 3,
+ std::numeric_limits<int64_t>::max(),
+ 0,
+ 16 + 8,
+ 16,
+ 0,
+ std::numeric_limits<uint64_t>::max(),
+ 0};
+ constexpr Array<size_t, 9> wordSizes{
+ sizeof("7") - 1,
+ sizeof(" 13") - 1,
+ sizeof(" 0x7FFFFFFFFFFFFFFF") - 1,
+ sizeof(" 0") - 1,
+ sizeof(" 018") - 1,
+ sizeof(" 010") - 1,
+ sizeof(" -8000000000000000") - 1,
+ sizeof(" 0xFFFFFFFFFFFFFFFF") - 1,
+ sizeof(" -8000000000000001") - 1};
+ constexpr Array<ExtStatus, 9> statuses{
+ ExtSuccess,
+ ExtSuccess,
+ ExtSuccess,
+ ExtSuccess,
+ ExtSuccess,
+ ExtSuccess,
+ ExtInvalidInput,
+ ExtSuccess,
+ ExtInvalidInput};
+ testStringToNumber(backing, wordSizes, statuses, expectedValues, ext_string_to_uint64, ExtRadixHexadecimal);
+}
+
+TEST(FudString, StringToInt8Invalid)
+{
+ testStringToIntegerInvalid<int8_t>(ext_string_to_int8);
+}
+
+TEST(FudString, StringToInt8RadixUnspec)
+{
+ Array<uint8_t, sizeof("7 13 127 0 018 010 -0x80 0x80")> backing{"7 13 127 0 018 010 -0x80 0x80"};
+ constexpr Array<int8_t, 8> expectedValues{7, 13, 127, 0, 0, 8, -0x80, 0};
+ constexpr Array<size_t, 8> wordSizes{
+ sizeof("7") - 1,
+ sizeof(" 13") - 1,
+ sizeof(" 127") - 1,
+ sizeof(" 0") - 1,
+ sizeof(" 018") - 1,
+ sizeof(" 010") - 1,
+ sizeof(" -0x80") - 1,
+ sizeof(" 0x80") - 1};
+ constexpr Array<ExtStatus, 8> statuses{
+ ExtSuccess,
+ ExtSuccess,
+ ExtSuccess,
+ ExtSuccess,
+ ExtInvalidInput,
+ ExtSuccess,
+ ExtSuccess,
+ ExtInvalidInput};
+ testStringToNumber(backing, wordSizes, statuses, expectedValues, ext_string_to_int8, 0);
+}
+
+TEST(FudString, StringToInt8Octal)
+{
+ Array<uint8_t, sizeof("7 13 177 0 018 010 -200 0xFF0 -201")> backing{"7 13 177 0 018 010 -200 0xFF0 -201"};
+ constexpr Array<int8_t, 9> expectedValues{7, 8 + 3, 127, 0, 0, 8, -128, 0};
+ constexpr Array<size_t, 9> wordSizes{
+ sizeof("7") - 1,
+ sizeof(" 13") - 1,
+ sizeof(" 177") - 1,
+ sizeof(" 0") - 1,
+ sizeof(" 018") - 1,
+ sizeof(" 010") - 1,
+ sizeof(" -200") - 1,
+ sizeof(" 0xFF0") - 1,
+ sizeof(" -201") - 1};
+ constexpr Array<ExtStatus, 9> statuses{
+ ExtSuccess,
+ ExtSuccess,
+ ExtSuccess,
+ ExtSuccess,
+ ExtInvalidInput,
+ ExtSuccess,
+ ExtSuccess,
+ ExtInvalidInput,
+ ExtInvalidInput};
+ testStringToNumber(backing, wordSizes, statuses, expectedValues, ext_string_to_int8, ExtRadixOctal);
+}
+
+TEST(FudString, StringToInt8Hex)
+{
+ Array<uint8_t, sizeof("7 13 0x7F 0 018 010 -80 0xFF0")> backing{"7 13 0x7F 0 018 010 -80 0xFF0"};
+ constexpr Array<int8_t, 8> expectedValues{7, 16 + 3, 127, 0, 16 + 8, 16, -128, 0};
+ constexpr Array<size_t, 8> wordSizes{
+ sizeof("7") - 1,
+ sizeof(" 13") - 1,
+ sizeof(" 0x7F") - 1,
+ sizeof(" 0") - 1,
+ sizeof(" 018") - 1,
+ sizeof(" 010") - 1,
+ sizeof(" -80") - 1,
+ sizeof(" 0xFF0") - 1};
+ constexpr Array<ExtStatus, 8>
+ statuses{ExtSuccess, ExtSuccess, ExtSuccess, ExtSuccess, ExtSuccess, ExtSuccess, ExtSuccess, ExtInvalidInput};
+ testStringToNumber(backing, wordSizes, statuses, expectedValues, ext_string_to_int8, ExtRadixHexadecimal);
+}
+
+TEST(FudString, StringToInt32Decimal)
+{
+ Array<uint8_t, sizeof("01 13 0x7F 0 018 010 -80 0xFF0")> backing{"01 13 0x7F 0 018 010 -80 0xFF0"};
+ constexpr Array<int8_t, 8> expectedValues{1, 13, 0, 0, 18, 10, -80, 0};
+ constexpr Array<size_t, 8> wordSizes{
+ sizeof("01") - 1,
+ sizeof(" 13") - 1,
+ sizeof(" 0x7F") - 1,
+ sizeof(" 0") - 1,
+ sizeof(" 018") - 1,
+ sizeof(" 010") - 1,
+ sizeof(" -80") - 1,
+ sizeof(" 0xFF0") - 1};
+ constexpr Array<ExtStatus, 8>
+ statuses{ExtSuccess, ExtSuccess, ExtInvalidInput, ExtSuccess, ExtSuccess, ExtSuccess, ExtSuccess, ExtInvalidInput};
+ testStringToNumber(backing, wordSizes, statuses, expectedValues, ext_string_to_int8, ExtRadixDecimal);
+}
+
+TEST(FudString, StringToInt64Hex)
+{
+ Array<uint8_t, sizeof("+7 13 0x7FFFFFFFFFFFFFFF 0 018 010 -8000000000000000 0x8000000000000000 -8000000000000001")>
+ backing{"+7 13 0x7FFFFFFFFFFFFFFF 0 018 010 -8000000000000000 0x8000000000000000 -8000000000000001"};
+ constexpr Array<int64_t, 9> expectedValues{
+ 7,
+ 16 + 3,
+ std::numeric_limits<int64_t>::max(),
+ 0,
+ 16 + 8,
+ 16,
+ std::numeric_limits<int64_t>::min(),
+ 0,
+ 0};
+ constexpr Array<size_t, 9> wordSizes{
+ sizeof("+7") - 1,
+ sizeof(" 13") - 1,
+ sizeof(" 0x7FFFFFFFFFFFFFFF") - 1,
+ sizeof(" 0") - 1,
+ sizeof(" 018") - 1,
+ sizeof(" 010") - 1,
+ sizeof(" -8000000000000000") - 1,
+ sizeof(" 0x8000000000000000") - 1,
+ sizeof(" -8000000000000001") - 1};
+ constexpr Array<ExtStatus, 9> statuses{
+ ExtSuccess,
+ ExtSuccess,
+ ExtSuccess,
+ ExtSuccess,
+ ExtSuccess,
+ ExtSuccess,
+ ExtSuccess,
+ ExtInvalidInput,
+ ExtInvalidInput};
+ testStringToNumber(backing, wordSizes, statuses, expectedValues, ext_string_to_int64, ExtRadixHexadecimal);
+}
+
+template <typename T>
+struct TestStringToFloatParams {
+ T value{0.0};
+ size_t wordSize{0};
+ ExtStatus status{ExtInvalidInput};
+ bool isNaN{false};
+};
+
+template <typename T, size_t BackingSize, size_t WordCount, typename Func>
+auto testStringToFloat(
+ Array<uint8_t, BackingSize>& backing,
+ const Array<TestStringToFloatParams<T>, WordCount>& testParams,
+ Func&& func)
+{
+ FudString extString{FudString::withBacking(backing)};
+ StringView view{extString};
+ T number = 0;
+
+ ASSERT_EQ(std::forward<Func>(func)(view, &number, nullptr), testParams[0].status);
+ ASSERT_EQ(number, testParams[0].value);
+
+ size_t index = 0;
+ for (size_t idx = 0; idx < WordCount; ++idx) {
+ ASSERT_EQ(std::forward<Func>(func)(view, &number, &index), testParams[idx].status);
+ if (testParams[idx].status == ExtSuccess) {
+ if (!testParams[idx].isNaN) {
+ ASSERT_EQ(number, testParams[idx].value);
+ } else {
+ ASSERT_NE(number, number);
+ }
+ ASSERT_EQ(index, testParams[idx].wordSize);
+ }
+
+ view.data += testParams[idx].wordSize;
+ view.length -= testParams[idx].wordSize;
+ }
+}
+
+TEST(FudString, StringToFloat)
+{
+ Array<uint8_t, sizeof("123 -7 123.5 .25 0.25 -.125 -2.0625 NaN -inf inF")> backing{
+ "123 -7 123.5 .25 0.25 -.125 -2.0625 NaN -inf inF"};
+ /* N.B. In the following expressions, sizeof includes null, but excludes one leading space. */
+ constexpr Array<TestStringToFloatParams<float>, 10> testParams{
+ {{123.0, sizeof("123") - 1, ExtSuccess},
+ {-7.0, sizeof("-7"), ExtSuccess},
+ {123.5, sizeof("123.5"), ExtSuccess},
+ {.25, sizeof(".25"), ExtSuccess},
+ {.25, sizeof("0.25"), ExtSuccess},
+ {-.125, sizeof("-.125"), ExtSuccess},
+ {-2.0625, sizeof("-2.0625"), ExtSuccess},
+ {std::numeric_limits<float>::quiet_NaN(), sizeof("NaN"), ExtSuccess, true},
+ {-1.0F * std::numeric_limits<float>::infinity(), sizeof("-inf"), ExtSuccess},
+ {std::numeric_limits<float>::infinity(), sizeof("inF"), ExtSuccess}}};
+
+ testStringToFloat(backing, testParams, ext_string_to_float);
+}
+
+TEST(FudString, StringToDouble)
+{
+ Array<uint8_t, sizeof("123 -7 123.5 .25 0.25 -.125 -2.0625 NaN -inf inF")> backing{
+ "123 -7 123.5 .25 0.25 -.125 -2.0625 NaN -inf inF"};
+ /* N.B. In the following expressions, sizeof includes null, but excludes one leading space. */
+ constexpr Array<TestStringToFloatParams<double>, 10> testParams{
+ {{123.0, sizeof("123") - 1, ExtSuccess},
+ {-7.0, sizeof("-7"), ExtSuccess},
+ {123.5, sizeof("123.5"), ExtSuccess},
+ {.25, sizeof(".25"), ExtSuccess},
+ {.25, sizeof("0.25"), ExtSuccess},
+ {-.125, sizeof("-.125"), ExtSuccess},
+ {-2.0625, sizeof("-2.0625"), ExtSuccess},
+ {std::numeric_limits<double>::quiet_NaN(), sizeof("NaN"), ExtSuccess, true},
+ {-1.0 * std::numeric_limits<double>::infinity(), sizeof("-inf"), ExtSuccess},
+ {std::numeric_limits<double>::infinity(), sizeof("inF"), ExtSuccess}}};
+
+ testStringToFloat(backing, testParams, ext_string_to_double);
+}
+
+TEST(FudString, StringToDoubleExpt)
+{
+ Array<uint8_t, sizeof("1.25e01 1.25e+01 0.125e2 12.5e0 125.e-1 125e-1 1250.e-02 1250.0e-02")> backing{
+ "1.25e01 1.25e+01 0.125e2 12.5e0 125.e-1 125e-1 1250.e-02 1250.0e-02"};
+ /* N.B. In the following expressions, sizeof includes null, but excludes one leading space. */
+ constexpr Array<TestStringToFloatParams<double>, 8> testParams{
+ {{12.5, sizeof("1.25e01") - 1, ExtSuccess},
+ {12.5, sizeof("1.25e+01"), ExtSuccess},
+ {12.5, sizeof("0.125e2"), ExtSuccess},
+ {12.5, sizeof("12.5e0"), ExtSuccess},
+ {12.5, sizeof("125.e-1"), ExtSuccess},
+ {12.5, sizeof("125e-1"), ExtSuccess},
+ {12.5, sizeof("1250.e-02"), ExtSuccess},
+ {12.5, sizeof("1250.0e-02"), ExtSuccess}
+ }};
+
+ testStringToFloat(backing, testParams, ext_string_to_double);
+}
+
+
+} // namespace fud
diff --git a/test/test_result.cpp b/test/test_result.cpp
index d86a170..172323d 100644
--- a/test/test_result.cpp
+++ b/test/test_result.cpp
@@ -1,5 +1,5 @@
/*
- * ExtLib
+ * libfud
* Copyright 2024 Dominick Allen
*
* Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/test/test_string.cpp b/test/test_string.cpp
new file mode 100644
index 0000000..3b8c3aa
--- /dev/null
+++ b/test/test_string.cpp
@@ -0,0 +1,296 @@
+/*
+ * 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_array.hpp"
+#include "fud_string.hpp"
+#include "test_common.hpp"
+
+#include "gtest/gtest.h"
+
+namespace fud {
+
+TEST(FudString, CStringLength)
+{
+ ASSERT_EQ(cStringLength(nullptr), -1);
+ ASSERT_EQ(cStringLength(""), 0);
+ ASSERT_EQ(cStringLength("a"), 1);
+ ASSERT_EQ(cStringLength(MULTI_BYTE_LITERAL), sizeof(MULTI_BYTE_LITERAL) - 1);
+ ASSERT_EQ(cStringLength(MULTI_BYTE_LITERAL), sizeof(MULTI_BYTE_LITERAL) - 1);
+}
+
+TEST(FudString, BasicStringOps)
+{
+ const Array<utf8, 2> invalid{0xFF, 0x00};
+ ASSERT_FALSE(Ascii::valid(invalid[0]));
+ const Array<utf8, 2> invalid2{0xFF, 0x00};
+ String fudString{invalid2.data()};
+ ASSERT_EQ(fudString.length(), 1);
+ ASSERT_EQ(fudString.data()[0], invalid[0]);
+ ASSERT_FALSE(Ascii::valid(fudString.data()[0]));
+ ASSERT_FALSE(fudString.utf8Valid());
+
+ fudString = String{"TEST"};
+ ASSERT_TRUE(fudString.utf8Valid());
+
+ StringView view1{};
+ ASSERT_FALSE(view1.utf8Valid());
+ StringView view2{fudString};
+ ASSERT_TRUE(view2.utf8Valid());
+ ASSERT_TRUE(view2.nullTerminated());
+}
+
+#if 0
+TEST(FudString, FindSubstringCxx)
+{
+ Array<uint8_t, sizeof(CHQUOTE)> basis{};
+ ASSERT_EQ(ExtCopyMem(basis.data(), basis.size(), CHQUOTE, sizeof(CHQUOTE)), ExtSuccess);
+ String fudString{basis.size() - 1, basis.size(), basis.data()};
+
+ StringView haystack{extString};
+ ASSERT_NE(haystack.length, 0);
+ ASSERT_NE(haystack.data, nullptr);
+
+ Array<uint8_t, sizeof("learning")> subBasis{};
+ ASSERT_EQ(ExtCopyMem(subBasis.data(), subBasis.size(), "learning", sizeof("learning")), ExtSuccess);
+ FudStringView needle{subBasis.size() - 1, subBasis.data()};
+
+ FudStringView stringView{};
+ auto findStatus = ext_string_find_substring(haystack, needle, &stringView);
+ ASSERT_EQ(findStatus, ExtSuccess);
+ // NOLINTBEGIN(cppcoreguidelines-pro-type-reinterpret-cast)
+ ASSERT_EQ(
+ ext_string_get_c_string(&extString) + sizeof("why waste time"),
+ reinterpret_cast<const char*>(stringView.data));
+ // NOLINTEND(cppcoreguidelines-pro-type-reinterpret-cast)
+}
+
+TEST(TestFudString, StringBuffer)
+{
+ Result<FixedStringBuffer<1>, ExtStatus> bufferResult{FixedStringBuffer<1>::fromLiteral(nullptr)};
+ ASSERT_TRUE(bufferResult.isError());
+ ASSERT_EQ(bufferResult.getError(), ExtNullPointer);
+
+ Array<uint8_t, 1> data1{};
+ bufferResult = FixedStringBuffer<1>::fromArray(data1);
+ ASSERT_TRUE(bufferResult.isOkay());
+ FixedStringBuffer<1> buffer1{std::move(bufferResult.getOkay())};
+ ASSERT_EQ(buffer1.m_string.m_data[0], '\0');
+ ASSERT_EQ(buffer1.m_string.m_length, 0);
+ ASSERT_EQ(buffer1.m_string.m_size, 1);
+
+ bufferResult = FixedStringBuffer<1>::fromLiteral("a");
+ ASSERT_TRUE(bufferResult.isError());
+ ASSERT_EQ(bufferResult.getError(), ExtInvalidInput);
+
+ data1[0] = 'a';
+ bufferResult = FixedStringBuffer<1>::fromArray(data1);
+ ASSERT_TRUE(bufferResult.isError());
+ ASSERT_EQ(bufferResult.getError(), ExtInvalidInput);
+
+ bufferResult = FixedStringBuffer<1>::fromLiteral("");
+ ASSERT_TRUE(bufferResult.isOkay());
+ buffer1 = {bufferResult.getOkay()};
+ ASSERT_EQ(buffer1.m_string.m_data[0], '\0');
+ ASSERT_EQ(buffer1.m_string.m_length, 0);
+ ASSERT_EQ(buffer1.m_string.m_size, 1);
+
+ StringView view1{buffer1};
+ ASSERT_EQ(buffer1.append(view1), ExtInvalidInput);
+
+ ASSERT_EQ(buffer1, buffer1);
+ const auto buffer2{buffer1};
+ ASSERT_EQ(buffer1, buffer2);
+ const auto buffer3{buffer2}; // NOLINT(performance-unnecessary-copy-initialization)
+ ASSERT_EQ(buffer1, buffer3);
+ auto buffer4 = buffer3; // NOLINT(performance-unnecessary-copy-initialization)
+ ASSERT_EQ(buffer1, buffer4);
+
+ Array<uint8_t, sizeof("test")> data2{"test"};
+ auto bufferResult2{FixedStringBuffer<data2.size()>::fromArray(data2)};
+ ASSERT_TRUE(bufferResult2.isOkay());
+ FixedStringBuffer<data2.size()> bufferTest{std::move(bufferResult2.getOkay())};
+ ASSERT_EQ(bufferTest.m_string.m_data[0], 't');
+ ASSERT_EQ(bufferTest.m_string.m_data[4], '\0');
+ ASSERT_EQ(bufferTest.m_string.m_length, data2.size() - 1);
+ ASSERT_EQ(bufferTest.m_string.m_size, data2.size());
+
+ const StringView view2{bufferTest};
+ FixedStringBuffer<data2.size()> bufferTest2{};
+ ASSERT_EQ(bufferTest2.append(view2), ExtSuccess);
+ ASSERT_EQ(bufferTest2.append(view2), ExtFull);
+ ASSERT_EQ(bufferTest2.remainingLength(), 0);
+ bufferTest2.m_string.m_length++;
+ ASSERT_EQ(bufferTest2.remainingLength(), 0);
+ bufferTest2.clear();
+ ASSERT_EQ(bufferTest2.remainingLength(), data2.size() - 1);
+
+ bufferTest2.m_string.m_length = 0;
+ bufferTest2.m_memory.clear();
+ buffer1.m_string.m_data[0] = 'a';
+ view1.length = 1;
+ ASSERT_EQ(bufferTest2.append(view1), FudStringInvalid);
+ ASSERT_TRUE(bufferTest2.m_memory.pushBack('\0'));
+ ASSERT_EQ(bufferTest2.append(view1), ExtSuccess);
+
+ ASSERT_NE(bufferTest2, bufferTest);
+ bufferTest2.clear();
+ ASSERT_EQ(bufferTest2.append(view2), ExtSuccess);
+ ASSERT_EQ(bufferTest, bufferTest2);
+ bufferTest.data()[3] = 'a';
+ ASSERT_NE(bufferTest, bufferTest2);
+}
+
+TEST(FudString, SpanCxx)
+{
+ Array<uint8_t, sizeof(CHQUOTE)> basis{};
+ ASSERT_EQ(ExtCopyMem(basis.data(), basis.size(), CHQUOTE, sizeof(CHQUOTE)), ExtSuccess);
+ String fudString{basis.size(), basis.size() - 1, basis.data()};
+
+ StringView inputView{extString};
+ ASSERT_NE(inputView.length, 0);
+ ASSERT_EQ(inputView.length, sizeof(CHQUOTE) - 1);
+ ASSERT_NE(inputView.data, nullptr);
+
+ constexpr Array<uint8_t, charSetSize + 1> charArray{CHARACTER_SET};
+ Array<Utf8, charSetSize> utf8Array{};
+ for (auto idx = 0; idx < utf8Array.size(); ++idx) {
+ utf8Array[idx] = Utf8{Ascii{charArray[idx]}};
+ }
+
+ static_assert(!ext_lib::hasDuplicates(charArray));
+ auto temp = utf8Array[0];
+ utf8Array[0] = utf8Array[1];
+ auto characterSetResult{Utf8Set<charSetSize>::make(utf8Array)};
+ ASSERT_TRUE(characterSetResult.isError());
+ utf8Array[0] = temp;
+ characterSetResult = Utf8Set<charSetSize>::make(utf8Array);
+ ASSERT_TRUE(characterSetResult.isOkay());
+ auto characterSet = characterSetResult.getOkay();
+
+ FudStringView result{};
+ EXPECT_EQ(ext_string_span_set(inputView, nullptr, nullptr), ExtNullPointer);
+ ASSERT_EQ(ext_string_span_set(inputView, &characterSet, &result), ExtSuccess);
+
+ ASSERT_EQ(result.length, sizeof(", when ignorance is instantaneous?") - 1);
+ std::vector<char> resString(result.length + 1);
+ ASSERT_EQ(ExtCopyMem(resString.data(), resString.size(), result.data, result.length), ExtSuccess);
+ ASSERT_EQ(std::string(resString.data()), std::string(", when ignorance is instantaneous?"));
+
+ Array<uint8_t, 2> ex2{'l'};
+ StringView setView2{};
+ setView2.length = sizeof(ex2) - 1;
+ setView2.data = ex2.data();
+ StringView setView3{setView2};
+ int difference = -1;
+ ASSERT_EQ(ExtCompareMem(&setView2, sizeof(setView2), &setView3, sizeof(setView3), &difference), ExtSuccess);
+ ASSERT_EQ(difference, 0);
+
+ Array<ExtUtf8Impl, 1> setArray{{Utf8{Ascii{'l'}}}};
+ Utf8Set<1> charSet{setArray};
+ ASSERT_FALSE(charSet.isEmptySet());
+ ASSERT_TRUE(static_cast<ExtUtf8Set*>(&charSet)->contains(Utf8{Ascii{'l'}}));
+ ASSERT_TRUE(static_cast<ExtUtf8Set*>(&charSet)->contains(setArray[0]));
+
+ EXPECT_EQ(ext_string_c_span_set(inputView, nullptr, nullptr), ExtNullPointer);
+ ASSERT_EQ(ext_string_c_span_set(inputView, &charSet, &result), ExtSuccess);
+
+ ASSERT_EQ(result.length, sizeof("learning, when ignorance is instantaneous?") - 1);
+ StringView resultView{result};
+ ASSERT_TRUE(resultView.nullTerminated());
+ resString.resize(result.length + 1);
+ ASSERT_EQ(ExtCopyMem(resString.data(), resString.size(), result.data, result.length), ExtSuccess);
+ ASSERT_EQ(std::string(resString.data()), std::string("learning, when ignorance is instantaneous?"));
+}
+
+TEST(FudString, SpanSetInvalid)
+{
+ constexpr Array<uint8_t, charSetSize + 1> charArray{CHARACTER_SET};
+ Array<Utf8, charSetSize> utf8Array{};
+ for (auto idx = 0; idx < utf8Array.size(); ++idx) {
+ utf8Array[idx] = Utf8{Ascii{charArray[idx]}};
+ }
+ utf8Array[0] = Utf8{Ascii{0xFF}}; // NOLINT(readability-magic-numbers)
+
+ static_assert(!ext_lib::hasDuplicates(charArray));
+ auto characterSetResult{Utf8Set<charSetSize>::make(utf8Array)};
+ ASSERT_TRUE(characterSetResult.isOkay());
+ auto characterSet{characterSetResult.getOkay()};
+
+ Array<uint8_t, sizeof(CHQUOTE)> backing{CHQUOTE};
+ auto fudString{FudString::withBacking(backing)};
+
+ StringView inputView{extString};
+
+ Array<uint8_t, sizeof(CHARACTER_SET)> setBacking{CHARACTER_SET};
+ setBacking[0] = ExtUtf8::invalidAsciiCode.character();
+
+ FudStringView result{};
+ ASSERT_EQ(ext_string_span_set(inputView, &characterSet, &result), ExtUtf8Invalid);
+ ASSERT_EQ(ext_string_c_span_set(inputView, &characterSet, &result), ExtUtf8Invalid);
+}
+
+TEST(FudString, SpanSetInputInvalid)
+{
+ constexpr Array<uint8_t, charSetSize + 1> charArray{CHARACTER_SET};
+ Array<Utf8, charSetSize> utf8Array{};
+ for (auto idx = 0; idx < utf8Array.size(); ++idx) {
+ utf8Array[idx] = Utf8{Ascii{charArray[idx]}};
+ }
+
+ static_assert(!ext_lib::hasDuplicates(charArray));
+ auto characterSetResult{Utf8Set<charSetSize>::make(utf8Array)};
+ ASSERT_TRUE(characterSetResult.isOkay());
+ auto characterSet{characterSetResult.getOkay()};
+
+ Array<uint8_t, sizeof(CHQUOTE)> backing{CHQUOTE};
+ backing[0] = ExtUtf8::invalidAsciiCode.character();
+ auto fudString{FudString::withBacking(backing)};
+
+ StringView inputView{extString};
+
+ Array<uint8_t, sizeof(CHARACTER_SET)> setBacking{CHARACTER_SET};
+
+ FudStringView result{};
+ ASSERT_EQ(ext_string_span_set(inputView, &characterSet, &result), ExtUtf8Invalid);
+ ASSERT_EQ(ext_string_c_span_set(inputView, &characterSet, &result), ExtUtf8Invalid);
+}
+
+TEST(FudString, SpanSetNotFound)
+{
+ constexpr Array<uint8_t, sizeof("QZ")> setBacking{"QZ"};
+ Array<Utf8, sizeof(setBacking) - 1> utf8Array{};
+ for (auto idx = 0; idx < utf8Array.size(); ++idx) {
+ utf8Array[idx] = Utf8{Ascii{setBacking[idx]}};
+ }
+
+ static_assert(!ext_lib::hasDuplicates(setBacking));
+ auto characterSetResult{Utf8Set<utf8Array.size()>::make(utf8Array)};
+ ASSERT_TRUE(characterSetResult.isOkay());
+ auto characterSet{characterSetResult.getOkay()};
+
+ Array<uint8_t, sizeof(CHQUOTE)> backing{CHQUOTE};
+ auto fudString{FudString::withBacking(backing)};
+
+ StringView inputView{extString};
+
+ FudStringView result{};
+ ASSERT_EQ(ext_string_span_set(inputView, &characterSet, &result), ExtNotFound);
+ ASSERT_EQ(ext_string_c_span_set(inputView, &characterSet, &result), ExtNotFound);
+}
+
+#endif
+} // namespace fud