diff options
author | Dominick Allen <djallen@librehumanitas.org> | 2024-09-23 07:36:16 -0500 |
---|---|---|
committer | Dominick Allen <djallen@librehumanitas.org> | 2024-09-23 07:36:16 -0500 |
commit | 0b860bb5dd6d2007db605291d239a6a9d41f57d1 (patch) | |
tree | fab140e03a3665236503d1405de9d33ba58ccc4a | |
parent | 7da829d48f9059c83ab9cada2c850621e8bbd3f3 (diff) |
Installable library.
-rw-r--r-- | CMakeLists.txt | 61 | ||||
-rw-r--r-- | cmake/FudConfig.cmake.in | 8 | ||||
-rw-r--r-- | cmake/fud-config.cmake | 1 | ||||
-rw-r--r-- | cmake/fud.pc.in | 10 | ||||
-rw-r--r-- | cmake/warnings.cmake | 1 | ||||
-rw-r--r-- | include/fud_assert.hpp | 31 | ||||
-rw-r--r-- | include/fud_c_file.hpp | 3 | ||||
-rw-r--r-- | include/fud_status.hpp | 8 | ||||
-rw-r--r-- | include/fud_string.hpp | 12 | ||||
-rw-r--r-- | include/fud_utf8.hpp | 86 | ||||
-rw-r--r-- | include/libfud.hpp | 20 | ||||
-rw-r--r-- | source/fud_assert.cpp | 26 | ||||
-rw-r--r-- | source/fud_c_file.cpp | 5 | ||||
-rw-r--r-- | source/fud_memory.cpp | 2 | ||||
-rw-r--r-- | source/fud_string.cpp | 148 | ||||
-rw-r--r-- | source/fud_utf8.cpp | 2 | ||||
-rw-r--r-- | source/fud_utf8_iterator.cpp | 14 | ||||
-rw-r--r-- | test/CMakeLists.txt | 7 | ||||
-rw-r--r-- | test/test_common.cpp | 18 | ||||
-rw-r--r-- | test/test_common.hpp | 43 | ||||
-rw-r--r-- | test/test_deserialize_number.cpp | 503 | ||||
-rw-r--r-- | test/test_result.cpp | 2 | ||||
-rw-r--r-- | test/test_string.cpp | 296 |
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 |