diff options
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | CMakeLists.txt | 12 | ||||
-rw-r--r-- | include/fud_array.hpp (renamed from include/array.hpp) | 8 | ||||
-rw-r--r-- | include/fud_c_file.hpp (renamed from include/c_file.hpp) | 16 | ||||
-rw-r--r-- | include/fud_fud_type_traits.hpp (renamed from include/fud_type_traits.hpp) | 0 | ||||
-rw-r--r-- | include/fud_memory.hpp (renamed from include/memory.hpp) | 18 | ||||
-rw-r--r-- | include/fud_result.hpp (renamed from include/result.hpp) | 6 | ||||
-rw-r--r-- | include/fud_status.hpp (renamed from include/status.hpp) | 42 | ||||
-rw-r--r-- | include/fud_string.hpp (renamed from include/string.hpp) | 62 | ||||
-rw-r--r-- | include/fud_unique_array.hpp (renamed from include/unique_array.hpp) | 4 | ||||
-rw-r--r-- | include/fud_utf8.hpp (renamed from include/utf8.hpp) | 157 | ||||
-rw-r--r-- | include/fud_utf8_iterator.hpp | 56 | ||||
-rw-r--r-- | include/libfud.hpp | 4 | ||||
-rw-r--r-- | include/utf8_iterator.hpp | 39 | ||||
-rw-r--r-- | source/fud_c_file.cpp (renamed from source/c_file.cpp) | 10 | ||||
-rw-r--r-- | source/fud_memory.cpp (renamed from source/memory.cpp) | 16 | ||||
-rw-r--r-- | source/fud_string.cpp | 1413 | ||||
-rw-r--r-- | source/fud_utf8.cpp (renamed from source/utf8.cpp) | 144 | ||||
-rw-r--r-- | source/fud_utf8_iterator.cpp | 55 | ||||
-rw-r--r-- | source/libfud.cpp | 4 | ||||
-rw-r--r-- | source/string.cpp | 19 | ||||
-rw-r--r-- | source/utf8_iterator.cpp | 38 | ||||
-rw-r--r-- | test/CMakeLists.txt | 66 | ||||
-rw-r--r-- | test/test_common.cpp | 18 | ||||
-rw-r--r-- | test/test_result.cpp | 52 |
25 files changed, 1940 insertions, 320 deletions
@@ -9,3 +9,4 @@ cppcheck.json test/fuzztest dist/ .semgrepignore +.#*
\ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index b135103..fc500ab 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,7 @@ endif() project(libfud VERSION 1.0.0 - DESCRIPTION "The Standard Library Extended and Exception Free" + DESCRIPTION "Library of FUD" LANGUAGES CXX C) set(CXX_CPPCHECK "project=build/compile_commands.json;enable=information;force") @@ -15,10 +15,11 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS true) add_library(libfud SHARED source/libfud.cpp - source/c_file.cpp - source/string.cpp - source/utf8.cpp - source/utf8_iterator.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}) @@ -35,7 +36,6 @@ set_target_properties( if (FUD_TEST) add_subdirectory(test) -add_subdirectory(examples) endif () if (FUD_DOC) diff --git a/include/array.hpp b/include/fud_array.hpp index 9de6c0a..4e2c702 100644 --- a/include/array.hpp +++ b/include/fud_array.hpp @@ -15,10 +15,10 @@ * limitations under the License. */ -#ifndef EXT_ARRAY_HPP -#define EXT_ARRAY_HPP +#ifndef FUD_ARRAY_HPP +#define FUD_ARRAY_HPP -#include "memory.hpp" +#include "fud_memory.hpp" #include <cstdlib> @@ -108,6 +108,6 @@ struct Array { constexpr auto operator<=>(const Array<T, Size>& other) const noexcept = default; }; -} // namespace ext_lib +} // namespace fud #endif diff --git a/include/c_file.hpp b/include/fud_c_file.hpp index 0f43e08..f89839d 100644 --- a/include/c_file.hpp +++ b/include/fud_c_file.hpp @@ -1,4 +1,5 @@ /* + * libfud * Copyright 2024 Dominick Allen * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -17,10 +18,11 @@ #ifndef FUD_C_FILE_HPP #define FUD_C_FILE_HPP -#include "result.hpp" +#include "fud_string.hpp" +#include "fud_result.hpp" #include <cstdint> -#include <string> +#include <cstdio> namespace fud { @@ -82,17 +84,17 @@ enum class FileResult class CBinaryFile { public: - CBinaryFile(const std::string& filename, CFileMode mode); - CBinaryFile(const std::string& filename, CFileMode mode, const std::string& extraFlags); + CBinaryFile(const String& filename, CFileMode mode); + CBinaryFile(const String& filename, CFileMode mode, const String& extraFlags); ~CBinaryFile(); FileResult open(); void close(); const FILE* file() const; private: - std::string m_filename; - std::string m_extraFlags{}; - std::string m_mode; + String m_filename; + String m_extraFlags{}; + String m_mode; CFileMode m_modeFlags; FILE* m_file{nullptr}; }; diff --git a/include/fud_type_traits.hpp b/include/fud_fud_type_traits.hpp index 3fdff79..3fdff79 100644 --- a/include/fud_type_traits.hpp +++ b/include/fud_fud_type_traits.hpp diff --git a/include/memory.hpp b/include/fud_memory.hpp index 1ca6029..d6708cd 100644 --- a/include/memory.hpp +++ b/include/fud_memory.hpp @@ -15,11 +15,11 @@ * limitations under the License. */ -#ifndef MEMORY_HPP -#define MEMORY_HPP +#ifndef FUD_MEMORY_HPP +#define FUD_MEMORY_HPP -#include "result.hpp" -#include "status.hpp" +#include "fud_result.hpp" +#include "fud_status.hpp" #include <cstddef> #include <cstdint> @@ -27,12 +27,22 @@ namespace fud { +extern void* fudAlloc(size_t size); +extern void* fudRealloc(size_t size); +extern void fudFree(void* ptr); + // An allocating function which returns null on failure. using FudAllocOne = void(*)(size_t); // An allocating function which returns null on failure. using FudAllocMany = void(*)(size_t, size_t); +/** \brief Copies from source to destination count bytes. + * + * \retcode FudStatus::Success + * \retcode FudStatus::NullPointer if destination or source are null + * \retcode FudStatus::InvalidInput if destination_size < count + */ FudStatus copyMem(void* destination, size_t destination_size, const void* source, size_t count); FudStatus compareMem(const void* lhs, size_t destination_size, const void* rhs, size_t count, int* difference); diff --git a/include/result.hpp b/include/fud_result.hpp index 158afd1..9c69800 100644 --- a/include/result.hpp +++ b/include/fud_result.hpp @@ -1,5 +1,5 @@ /* - * LibFud + * libfud * Copyright 2024 Dominick Allen * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,8 +15,8 @@ * limitations under the License. */ -#ifndef BOOKMOUSE_RESULT_HPP -#define BOOKMOUSE_RESULT_HPP +#ifndef FUD_RESULT_HPP +#define FUD_RESULT_HPP #include <utility> #include <variant> diff --git a/include/status.hpp b/include/fud_status.hpp index 2bba4b3..47b14a4 100644 --- a/include/status.hpp +++ b/include/fud_status.hpp @@ -15,8 +15,8 @@ * limitations under the License. */ -#ifndef STATUS_HPP -#define STATUS_HPP +#ifndef FUD_STATUS_HPP +#define FUD_STATUS_HPP namespace fud { @@ -41,43 +41,43 @@ enum class [[nodiscard]] FudStatus NotSupported }; -static inline const char* ExtStatusToString(FudStatus status) +static inline const char* FudStatusToString(FudStatus status) { switch (status) { case FudStatus::Success: - return "ExtSuccess"; + return "Success"; case FudStatus::NullPointer: - return "ExtNullPointer"; + return "NullPointer"; case FudStatus::StringInvalid: - return "ExtStringInvalid"; + return "StringInvalid"; case FudStatus::OperationInvalid: - return "ExtOperationInvalid"; + return "OperationInvalid"; case FudStatus::AllocFailure: - return "ExtAllocFailure"; + return "AllocFailure"; case FudStatus::InvalidInput: - return "ExtInvalidInput"; + return "InvalidInput"; case FudStatus::Utf8Invalid: - return "ExtUtf8Invalid"; + return "Utf8Invalid"; case FudStatus::Failure: - return "ExtFailure"; + return "Failure"; case FudStatus::NotFound: - return "ExtNotFound"; + return "NotFound"; case FudStatus::Aliased: - return "ExtAliased"; + return "Aliased"; case FudStatus::Empty: - return "ExtEmpty"; + return "Empty"; case FudStatus::Partial: - return "ExtPartial"; + return "Partial"; case FudStatus::Full: - return "ExtFull"; + return "Full"; case FudStatus::RangeError: - return "ExtRangeError"; + return "RangeError"; case FudStatus::VariantInvalid: - return "ExtVariantInvalid"; + return "VariantInvalid"; case FudStatus::NotImplemented: - return "ExtNotImplemented"; + return "NotImplemented"; case FudStatus::NotSupported: - return "ExtNotSupported"; + return "NotSupported"; default: return "Unknown"; } @@ -101,6 +101,6 @@ bool anyAreNull(T pointer, Ts... pointers) return anyAreNull(pointers...); } -} // namespace ext_lib +} // namespace fud #endif diff --git a/include/string.hpp b/include/fud_string.hpp index 89aa94e..42009b4 100644 --- a/include/string.hpp +++ b/include/fud_string.hpp @@ -1,4 +1,5 @@ /* + * libfud * Copyright 2024 Dominick Allen * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -17,7 +18,7 @@ #ifndef FUD_STRING_HPP #define FUD_STRING_HPP -#include "utf8.hpp" +#include "fud_utf8.hpp" #include <climits> #include <cstddef> @@ -26,8 +27,21 @@ static_assert(CHAR_BIT == 8); namespace fud { +constexpr size_t SSO_BUF_LENGTH = 15; +constexpr size_t SSO_BUF_SIZE = SSO_BUF_LENGTH + 1; + class String { public: + String() = default; + explicit String(const utf8* cString); + explicit String(const char* cString); + String(const String& rhs); + String(String&& rhs); + ~String(); + + String& operator=(const String& rhs); + String& operator=(String&& rhs); + [[nodiscard]] constexpr size_t length() const { return m_length; @@ -43,9 +57,28 @@ class String { return m_capacity; } - [[nodiscard]] constexpr utf8* data() const + /** \brier The underlying data, guaranteed to have c string representation. */ + [[nodiscard]] constexpr const utf8* data() const { - return m_data; + return isLarge() ? m_data : m_buffer.data(); + } + + /** \brier The underlying data as an explicit c string. */ + [[nodiscard]] inline const char* c_str() const + { + return isLarge() ? reinterpret_cast<const char*>(m_data) : reinterpret_cast<const char*>(m_buffer.data()); + } + + /** \brier The underlying data, guaranteed to have c string representation. */ + [[nodiscard]] constexpr utf8* data() + { + return isLarge() ? m_data : m_buffer.data(); + } + + /** \brier The underlying data as an explicit c string. */ + [[nodiscard]] inline char* c_str() + { + return isLarge() ? reinterpret_cast<char*>(m_data) : reinterpret_cast<char*>(m_buffer.data()); } [[nodiscard]] bool nullTerminated() const; @@ -69,16 +102,27 @@ class String { [[nodiscard]] FudStatus pushBack(utf8 letter); - [[nodiscard]] FudStatus pushBack(const ExtUtf8& letter); + [[nodiscard]] FudStatus pushBack(const FudUtf8& letter); std::optional<utf8> pop(); [[nodiscard]] FudStatus catenate(StringView source); + [[nodiscard]] String append(const String& rhs) const; + private: - utf8* m_data; - size_t m_length; - size_t m_capacity; + using BufType = Array<utf8, SSO_BUF_SIZE>; + union { + BufType m_buffer{BufType::constFill(0)}; + utf8* m_data; + }; + size_t m_length{0}; + size_t m_capacity{0}; + + [[nodiscard]] constexpr bool isLarge() const + { + return m_capacity > SSO_BUF_LENGTH; + } }; class StringView { @@ -148,6 +192,10 @@ class StringView { FudStatus skipWhitespace(StringView& view, size_t& skipIndex); +ssize_t cStringLength(const char* str); + +ssize_t cStringLength(const char* str, size_t maxLength); + } // namespace fud #endif diff --git a/include/unique_array.hpp b/include/fud_unique_array.hpp index a7e0731..428374a 100644 --- a/include/unique_array.hpp +++ b/include/fud_unique_array.hpp @@ -18,8 +18,8 @@ #ifndef FUD_UNIQUE_ARRAY_HPP #define FUD_UNIQUE_ARRAY_HPP -#include "array.hpp" -#include "fud_type_traits.hpp" +#include "fud_array.hpp" +#include "fud_fud_type_traits.hpp" #include <cstdlib> #include <utility> diff --git a/include/utf8.hpp b/include/fud_utf8.hpp index c66d93c..da1a5fe 100644 --- a/include/utf8.hpp +++ b/include/fud_utf8.hpp @@ -18,15 +18,10 @@ #ifndef FUD_UTF8_HPP #define FUD_UTF8_HPP -#include "array.hpp" -#include "memory.hpp" -#include "status.hpp" -#include "unique_array.hpp" - -/* -#include "ext_hash.hpp" -#include "ext_set.hpp" -*/ +#include "fud_array.hpp" +#include "fud_memory.hpp" +#include "fud_status.hpp" +#include "fud_unique_array.hpp" #include <cstdint> #include <optional> @@ -238,33 +233,33 @@ struct Utf84Byte { using Utf8Variant = std::variant<Ascii, Utf82Byte, Utf83Byte, Utf84Byte>; -constexpr auto ExtUtf8TypeSet{UniqueArray<size_t, 0, 1, 2, 3>{}}; -enum class ExtUtf8Type : uint8_t +constexpr auto Utf8TypeSet{UniqueArray<size_t, 0, 1, 2, 3>{}}; +enum class Utf8Type : uint8_t { Ascii, Utf82Byte, Utf83Byte, Utf84Byte, }; -static_assert(ExtUtf8TypeSet.m_values[0] == static_cast<uint8_t>(ExtUtf8Type::Ascii)); -static_assert(ExtUtf8TypeSet.m_values[1] == static_cast<uint8_t>(ExtUtf8Type::Utf82Byte)); -static_assert(ExtUtf8TypeSet.m_values[2] == static_cast<uint8_t>(ExtUtf8Type::Utf83Byte)); -static_assert(ExtUtf8TypeSet.m_values[3] == static_cast<uint8_t>(ExtUtf8Type::Utf84Byte)); +static_assert(Utf8TypeSet.m_values[0] == static_cast<uint8_t>(Utf8Type::Ascii)); +static_assert(Utf8TypeSet.m_values[1] == static_cast<uint8_t>(Utf8Type::Utf82Byte)); +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 ExtUtf8 { +struct FudUtf8 { Utf8Variant m_variant{Utf8Variant{Ascii{}}}; static constexpr Ascii invalidAsciiCode{Ascii{0xFF}}; - static ExtUtf8 fromString(const String& fudString, size_t index) noexcept; - static ExtUtf8 fromStringView(StringView&& fudView, size_t index) noexcept; - static ExtUtf8 fromStringView(const StringView& fudView, size_t index) noexcept; + static FudUtf8 fromString(const String& fudString, size_t index) noexcept; + static FudUtf8 fromStringView(StringView&& fudView, size_t index) noexcept; + static FudUtf8 fromStringView(const StringView& fudView, size_t index) noexcept; - static constexpr ExtUtf8 makeUtf8(Array<utf8, 4>& data) + static constexpr FudUtf8 makeUtf8(Array<utf8, 4>& data) { - ExtUtf8 unicode{}; + FudUtf8 unicode{}; if (Ascii::valid(data[0])) { unicode.m_variant = Ascii{data[0]}; } else if (Utf82Byte::valid(data[0], data[1])) { @@ -279,9 +274,9 @@ struct ExtUtf8 { return unicode; } - static constexpr ExtUtf8 makeUtf8(const Ascii& utf8Char) + static constexpr FudUtf8 makeUtf8(const Ascii& utf8Char) { - ExtUtf8 unicode{{Utf8Variant{Ascii{}}}}; + FudUtf8 unicode{{Utf8Variant{Ascii{}}}}; if (utf8Char.valid()) { unicode.m_variant = utf8Char; } else { @@ -290,33 +285,33 @@ struct ExtUtf8 { return unicode; } - static constexpr ExtUtf8 invalidAscii() + static constexpr FudUtf8 invalidAscii() { - ExtUtf8 utf8{}; + FudUtf8 utf8{}; utf8.m_variant = Ascii{invalidAsciiCode}; return utf8; } - [[nodiscard]] constexpr ExtUtf8Type getType() const + [[nodiscard]] constexpr Utf8Type getType() const { - return static_cast<ExtUtf8Type>(m_variant.index()); + return static_cast<Utf8Type>(m_variant.index()); } [[nodiscard]] constexpr bool isAscii() const { - return getType() == ExtUtf8Type::Ascii; + return getType() == Utf8Type::Ascii; } [[nodiscard]] constexpr bool valid() const noexcept { switch (m_variant.index()) { - case static_cast<size_t>(ExtUtf8Type::Ascii): + case static_cast<size_t>(Utf8Type::Ascii): return std::get<Ascii>(m_variant).valid(); - case static_cast<size_t>(ExtUtf8Type::Utf82Byte): + case static_cast<size_t>(Utf8Type::Utf82Byte): return std::get<Utf82Byte>(m_variant).valid(); - case static_cast<size_t>(ExtUtf8Type::Utf83Byte): + case static_cast<size_t>(Utf8Type::Utf83Byte): return std::get<Utf83Byte>(m_variant).valid(); - case static_cast<size_t>(ExtUtf8Type::Utf84Byte): + case static_cast<size_t>(Utf8Type::Utf84Byte): return std::get<Utf84Byte>(m_variant).valid(); default: // unlikely return false; @@ -329,13 +324,13 @@ struct ExtUtf8 { return 0; } switch (m_variant.index()) { - case static_cast<size_t>(ExtUtf8Type::Ascii): + case static_cast<size_t>(Utf8Type::Ascii): return Ascii::size(); - case static_cast<size_t>(ExtUtf8Type::Utf82Byte): + case static_cast<size_t>(Utf8Type::Utf82Byte): return Utf82Byte::size(); - case static_cast<size_t>(ExtUtf8Type::Utf83Byte): + case static_cast<size_t>(Utf8Type::Utf83Byte): return Utf83Byte::size(); - case static_cast<size_t>(ExtUtf8Type::Utf84Byte): + case static_cast<size_t>(Utf8Type::Utf84Byte): return Utf84Byte::size(); default: // unlikely return 0; @@ -349,13 +344,13 @@ struct ExtUtf8 { } switch (m_variant.index()) { - case static_cast<size_t>(ExtUtf8Type::Ascii): + case static_cast<size_t>(Utf8Type::Ascii): return std::get<Ascii>(m_variant).characters.data(); - case static_cast<size_t>(ExtUtf8Type::Utf82Byte): + case static_cast<size_t>(Utf8Type::Utf82Byte): return std::get<Utf82Byte>(m_variant).characters.data(); - case static_cast<size_t>(ExtUtf8Type::Utf83Byte): + case static_cast<size_t>(Utf8Type::Utf83Byte): return std::get<Utf83Byte>(m_variant).characters.data(); - case static_cast<size_t>(ExtUtf8Type::Utf84Byte): + case static_cast<size_t>(Utf8Type::Utf84Byte): return std::get<Utf84Byte>(m_variant).characters.data(); default: // unlikely return nullptr; @@ -374,7 +369,7 @@ struct ExtUtf8 { [[nodiscard]] constexpr int64_t hash() const noexcept { - using fud::ExtUtf8Type; + using fud::Utf8Type; using fud::Utf82Byte; using fud::Utf83Byte; using fud::Utf84Byte; @@ -387,17 +382,17 @@ struct ExtUtf8 { constexpr uint8_t TwoByteShift = 2 * OneByteShift; constexpr uint8_t ThreeByteShift = 3 * OneByteShift; - switch (static_cast<ExtUtf8Type>(m_variant.index())) { - case ExtUtf8Type::Ascii: + switch (static_cast<Utf8Type>(m_variant.index())) { + case Utf8Type::Ascii: return std::get<Ascii>(m_variant).characters[0]; - case ExtUtf8Type::Utf82Byte: + case Utf8Type::Utf82Byte: return static_cast<int64_t>(std::get<Utf82Byte>(m_variant).characters[0]) << OneByteShift | static_cast<int64_t>(std::get<Utf82Byte>(m_variant).characters[1]); - case ExtUtf8Type::Utf83Byte: + case Utf8Type::Utf83Byte: return static_cast<int64_t>(std::get<Utf83Byte>(m_variant).characters[0]) << TwoByteShift | static_cast<int64_t>(std::get<Utf83Byte>(m_variant).characters[1]) << OneByteShift | static_cast<int64_t>(std::get<Utf83Byte>(m_variant).characters[2]); - case ExtUtf8Type::Utf84Byte: + case Utf8Type::Utf84Byte: return static_cast<int64_t>(std::get<Utf84Byte>(m_variant).characters[0]) << ThreeByteShift | static_cast<int64_t>(std::get<Utf84Byte>(m_variant).characters[1]) << TwoByteShift | static_cast<int64_t>(std::get<Utf84Byte>(m_variant).characters[2]) << OneByteShift | @@ -407,15 +402,15 @@ struct ExtUtf8 { } } - constexpr bool operator==(const ExtUtf8& other) const noexcept = default; + constexpr bool operator==(const FudUtf8& other) const noexcept = default; - constexpr auto operator<=>(const ExtUtf8& other) const noexcept + constexpr auto operator<=>(const FudUtf8& other) const noexcept { - auto hasSameAlternative = []<typename T>(const ExtUtf8& lhs, const ExtUtf8& rhs) noexcept { + auto hasSameAlternative = []<typename T>(const FudUtf8& lhs, const FudUtf8& rhs) noexcept { return std::holds_alternative<T>(lhs.m_variant) && std::holds_alternative<T>(rhs.m_variant); }; - auto getSameAlternative = []<typename T>(const ExtUtf8& lhs, const ExtUtf8& rhs) noexcept { + auto getSameAlternative = []<typename T>(const FudUtf8& lhs, const FudUtf8& rhs) noexcept { return std::get<T>(lhs.m_variant).operator<=>(std::get<T>(rhs.m_variant)); }; @@ -460,7 +455,7 @@ struct ExtUtf8 { std::optional<Ascii> getAscii() const { - if (m_variant.index() == static_cast<size_t>(ExtUtf8Type::Ascii)) { + if (m_variant.index() == static_cast<size_t>(Utf8Type::Ascii)) { return std::get<Ascii>(m_variant); } return std::nullopt; @@ -468,89 +463,89 @@ struct ExtUtf8 { }; /** \brief Checks if a character is ascii. */ -bool ext_lib_char_is_ascii(char character); +bool char_is_ascii(char character); -FudStatus ext_lib_utf8_is_ascii(ExtUtf8& character, bool& isAscii); +FudStatus utf8_is_ascii(FudUtf8& character, bool& isAscii); /** \brief Checks if a character is alphanumeric. */ -bool ext_lib_char_is_alphanumeric(char character); +bool char_is_alphanumeric(char character); /** \brief Checks if a character is alphanumeric. */ -FudStatus ext_lib_utf8_is_alphanumeric(ExtUtf8* character, bool* pred); +FudStatus utf8_is_alphanumeric(FudUtf8* character, bool* pred); /** \brief Checks if a character is alphabetic. */ -bool ext_lib_char_is_alpha(char character); +bool char_is_alpha(char character); /** \brief Checks if a character is alphabetic. */ -FudStatus ext_lib_utf8_is_alpha(ExtUtf8* character, bool* pred); +FudStatus utf8_is_alpha(FudUtf8* character, bool* pred); /** \brief Checks if a character is lowercase. */ -bool ext_lib_char_is_lowercase(char character); +bool char_is_lowercase(char character); /** \brief Checks if a character is lowercase. */ -FudStatus ext_lib_utf8_is_lowercase(ExtUtf8* character, bool* pred); +FudStatus utf8_is_lowercase(FudUtf8* character, bool* pred); /** \brief Checks if a character is an uppercase character. */ -bool ext_lib_char_is_uppercase(char character); +bool char_is_uppercase(char character); /** \brief Checks if a character is uppercase. */ -FudStatus ext_lib_utf8_is_uppercase(ExtUtf8* character, bool* pred); +FudStatus utf8_is_uppercase(FudUtf8* character, bool* pred); /** \brief Checks if a character is a digit. */ -bool ext_lib_char_is_digit(char character); +bool char_is_digit(char character); /** \brief Checks if a character is a digit. */ -FudStatus ext_lib_utf8_is_digit(ExtUtf8* character, bool* pred); +FudStatus utf8_is_digit(FudUtf8* character, bool* pred); /** \brief Checks if a character is a hexadecimal character. */ -bool ext_lib_char_is_hex_digit(char character); +bool char_is_hex_digit(char character); /** \brief Checks if a character is a hexadecimal digit. */ -FudStatus ext_lib_utf8_is_hex_digit(ExtUtf8* character, bool* pred); +FudStatus utf8_is_hex_digit(FudUtf8* character, bool* pred); /** \brief Checks if a character is a control character. */ -bool ext_lib_char_is_control(char character); +bool char_is_control(char character); /** \brief Checks if a character is a control character. */ -FudStatus ext_lib_utf8_is_control(ExtUtf8* character, bool* pred); +FudStatus utf8_is_control(FudUtf8* character, bool* pred); /** \brief Checks if a character is a graphical character. */ -bool ext_lib_char_is_graphical(char character); +bool char_is_graphical(char character); /** \brief Checks if a character is a graphical character. */ -FudStatus ext_lib_utf8_is_graphical(ExtUtf8* character, bool* pred); +FudStatus utf8_is_graphical(FudUtf8* character, bool* pred); /** \brief Checks if a character is a space character. */ -bool ext_lib_char_is_space(char character); +bool char_is_space(char character); /** \brief Checks if a character is a space character. */ -FudStatus ext_lib_utf8_is_space(ExtUtf8* character, bool* pred); +FudStatus utf8_is_space(FudUtf8* character, bool* pred); /** \brief Checks if a character is a blank character. */ -bool ext_lib_char_is_blank(char character); +bool char_is_blank(char character); /** \brief Checks if a character is a blank character. */ -FudStatus ext_lib_utf8_is_blank(ExtUtf8* character, bool* pred); +FudStatus utf8_is_blank(FudUtf8* character, bool* pred); /** \brief Checks if a character is a printable character. */ -bool ext_lib_char_is_printable(char character); +bool char_is_printable(char character); /** \brief Checks if a character is a printable character. */ -FudStatus ext_lib_utf8_is_printable(ExtUtf8* character, bool* pred); +FudStatus utf8_is_printable(FudUtf8* character, bool* pred); /** \brief Checks if a character is a punctuation character. */ -bool ext_lib_char_is_punctuation(char character); +bool char_is_punctuation(char character); /** \brief Checks if a character is a punctuation character. */ -FudStatus ext_lib_utf8_is_punctuation(ExtUtf8* character, bool* pred); +FudStatus utf8_is_punctuation(FudUtf8* character, bool* pred); -uint8_t ext_lib_char_to_lower(uint8_t character); +uint8_t char_to_lower(uint8_t character); -ExtUtf8* ext_lib_utf8_to_lower(ExtUtf8* character); +FudUtf8* utf8_to_lower(FudUtf8* character); -uint8_t ext_lib_char_to_upper(uint8_t character); +uint8_t char_to_upper(uint8_t character); -ExtUtf8* ext_lib_utf8_to_upper(ExtUtf8* character); +FudUtf8* utf8_to_upper(FudUtf8* character); } // namespace fud diff --git a/include/fud_utf8_iterator.hpp b/include/fud_utf8_iterator.hpp new file mode 100644 index 0000000..3721b00 --- /dev/null +++ b/include/fud_utf8_iterator.hpp @@ -0,0 +1,56 @@ +/* + * 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_UTF8_ITERATOR_HPP +#define FUD_UTF8_ITERATOR_HPP + +#include "fud_string.hpp" +#include "fud_utf8.hpp" + +#include <cstddef> +#include <optional> + +namespace fud { + +class Utf8Iterator { + private: + size_t m_index{0}; + // NOLINTBEGIN(cppcoreguidelines-avoid-const-or-ref-data-members) + const StringView m_view; + // NOLINTEND(cppcoreguidelines-avoid-const-or-ref-data-members) + + public: + explicit constexpr Utf8Iterator(const String& fudString) : m_view{fudString} + { + } + + explicit constexpr Utf8Iterator(const StringView& view) : m_view{view} + { + } + + constexpr void reset() + { + m_index = 0; + } + + [[nodiscard]] std::optional<FudUtf8> peek() const; + std::optional<FudUtf8> next(); +}; + +} // namespace fud + +#endif diff --git a/include/libfud.hpp b/include/libfud.hpp index ffea195..be840a4 100644 --- a/include/libfud.hpp +++ b/include/libfud.hpp @@ -1,5 +1,5 @@ /* - * LibFud + * libfud * Copyright 2024 Dominick Allen * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,7 +18,7 @@ #ifndef LIBFUD_HPP #define LIBFUD_HPP -#include "result.hpp" // IWYU pragma: export +#include "fud_result.hpp" // IWYU pragma: export namespace fud { diff --git a/include/utf8_iterator.hpp b/include/utf8_iterator.hpp deleted file mode 100644 index 1f9674b..0000000 --- a/include/utf8_iterator.hpp +++ /dev/null @@ -1,39 +0,0 @@ -#ifndef FUD_UTF8_ITERATOR_HPP -#define FUD_UTF8_ITERATOR_HPP - -#include "string.hpp" -#include "utf8.hpp" - -#include <cstddef> -#include <optional> - -namespace fud { - -class Utf8Iterator { - private: - size_t m_index{0}; - // NOLINTBEGIN(cppcoreguidelines-avoid-const-or-ref-data-members) - const StringView m_view; - // NOLINTEND(cppcoreguidelines-avoid-const-or-ref-data-members) - - public: - explicit constexpr Utf8Iterator(const String& extString) : m_view{extString} - { - } - - explicit constexpr Utf8Iterator(const StringView& view) : m_view{view} - { - } - - constexpr void reset() - { - m_index = 0; - } - - [[nodiscard]] std::optional<ExtUtf8> peek() const; - std::optional<ExtUtf8> next(); -}; - -} // namespace fud - -#endif diff --git a/source/c_file.cpp b/source/fud_c_file.cpp index f64e024..ff54d8e 100644 --- a/source/c_file.cpp +++ b/source/fud_c_file.cpp @@ -1,5 +1,5 @@ /* - * LibFud + * libfud * Copyright 2024 Dominick Allen * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,21 +15,21 @@ * limitations under the License. */ -#include "c_file.hpp" +#include "fud_c_file.hpp" namespace fud { -CBinaryFile::CBinaryFile(const std::string& filename, CFileMode mode) +CBinaryFile::CBinaryFile(const String& filename, CFileMode mode) : m_filename{filename}, m_mode{CBinaryFileModeFromFlags(mode)}, m_modeFlags{mode} { } -CBinaryFile::CBinaryFile(const std::string& filename, CFileMode mode, const std::string& extraFlags) +CBinaryFile::CBinaryFile(const String& filename, CFileMode mode, const String& extraFlags) : m_filename{filename}, m_extraFlags{extraFlags}, - m_mode{std::string(CBinaryFileModeFromFlags(mode) + extraFlags)}, + m_mode{String(CBinaryFileModeFromFlags(mode)).append(extraFlags)}, m_modeFlags{mode} { } diff --git a/source/memory.cpp b/source/fud_memory.cpp index 9f5d358..fe6dfae 100644 --- a/source/memory.cpp +++ b/source/fud_memory.cpp @@ -1,5 +1,5 @@ /* - * ExtLib + * libfud * Copyright 2024 Dominick Allen * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,9 +15,7 @@ * limitations under the License. */ -#include "memory.hpp" - -#include <cstdint> +#include "fud_memory.hpp" namespace fud { @@ -31,8 +29,8 @@ FudStatus copyMem(void* destination, size_t destination_size, const void* source return FudStatus::InvalidInput; } - auto* destPtr = static_cast<uint8_t*>(destination); - const auto* sourcePtr = static_cast<const uint8_t*>(source); + auto* destPtr = static_cast<char*>(destination); + const auto* sourcePtr = static_cast<const char*>(source); for (decltype(destination_size) idx = 0; idx < count; ++idx) { destPtr[idx] = sourcePtr[idx]; } @@ -53,7 +51,7 @@ FudStatus compareMem(const void* lhs, size_t destination_size, const void* rhs, int localDifference = 0; // NOLINTBEGIN(readability-magic-numbers) for (decltype(destination_size) idx = 0; idx < count; idx++) { - localDifference = static_cast<const uint8_t*>(lhs)[idx] - static_cast<const uint8_t*>(rhs)[idx]; + localDifference = static_cast<const char*>(lhs)[idx] - static_cast<const char*>(rhs)[idx]; if (localDifference != 0) { *difference = localDifference; return FudStatus::Success; @@ -90,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<uint8_t*>(data)[idx] = pattern; + static_cast<char*>(data)[idx] = pattern; } return FudStatus::Success; @@ -120,7 +118,7 @@ FudStatus setMemory( auto remainingSize = dataSize - byteOffset; - auto* offsetData = static_cast<uint8_t*>(data) + byteOffset; + auto* offsetData = static_cast<char*>(data) + byteOffset; return setMemory(offsetData, remainingSize, pattern, byteCount); } diff --git a/source/fud_string.cpp b/source/fud_string.cpp new file mode 100644 index 0000000..d690aa9 --- /dev/null +++ b/source/fud_string.cpp @@ -0,0 +1,1413 @@ +/* + * LibFud + * Copyright 2024 Dominick Allen + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "fud_string.hpp" + +#include <cassert> + +namespace fud { + +ssize_t cStringLength(const char* str) +{ + constexpr auto maxLength = SSIZE_MAX - 1; + return cStringLength(str, maxLength); +} + +ssize_t cStringLength(const char* str, size_t maxLength) +{ + if (str == nullptr || maxLength > (SSIZE_MAX - 1)) { + return -1; + } + + ssize_t size = 0; + + while (str[size] != 0 && static_cast<size_t>(size) < maxLength) { + size++; + } + + if (str[size] != 0 && static_cast<size_t>(size) == maxLength) { + return static_cast<ssize_t>(maxLength) + 1; + } + + return size; +} + +String::String(const utf8* cString) : String(reinterpret_cast<const char*>(cString)) +{ +} + +String::String(const char* cString) +{ + auto lenResult = cStringLength(cString); + if (lenResult < 0 || lenResult >= SSIZE_MAX) { + m_length = 1; + m_capacity = 0; + } else if (lenResult < SSO_BUF_SIZE) { + m_length = static_cast<size_t>(lenResult); + assert(copyMem(m_buffer.data(), m_buffer.size(), cString, m_length)); + } 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); + } +} + +String::String(const String& rhs) : m_length{rhs.m_length}, m_capacity{rhs.m_capacity} +{ + if (rhs.valid()) { + if (isLarge()) { + m_data = static_cast<utf8*>(fudAlloc(m_capacity)); + assert(m_data != nullptr); + } + assert(copyMem(data(), m_capacity, rhs.data(), m_length) == FudStatus::Success); + assert(nullTerminate() == FudStatus::Success); + } +} + +String::String(String&& rhs) : m_length{rhs.m_length}, m_capacity{rhs.m_capacity} +{ + if (rhs.isLarge()) { + 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); + } +} + +String::~String() +{ + if (isLarge() && m_data != nullptr) { + fudFree(m_data); + m_data = nullptr; + } +} + +String& String::operator=(const String& rhs) +{ + m_length = rhs.m_length; + m_capacity = rhs.m_capacity; + if (rhs.valid()) { + if (isLarge()) { + m_data = static_cast<utf8*>(fudAlloc(m_capacity)); + assert(m_data != nullptr); + } + assert(copyMem(data(), m_capacity, rhs.data(), m_length) == FudStatus::Success); + assert(nullTerminate() == FudStatus::Success); + } + return *this; +} + +String& String::operator=(String&& rhs) { + m_length = rhs.m_length; + m_capacity = rhs.m_capacity; + if (rhs.isLarge()) { + 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); + } + return *this; +} + +bool String::nullTerminated() const +{ + return data() != nullptr && m_length < m_capacity && data()[m_length] == '\0'; +} + +bool String::valid() const +{ + return nullTerminated() && m_length < m_capacity; +} + +bool String::utf8Valid() const +{ + if (!valid()) { + return false; + } + + StringView view{*this}; + return view.utf8Valid(); +} + +FudStatus String::nullTerminate() const +{ + if (m_length < m_capacity) { + m_data[m_length] = '\0'; + return FudStatus::Success; + } + return FudStatus::StringInvalid; +} + +std::optional<utf8> String::pop() +{ + if (m_length < 1) { + return std::nullopt; + } + m_length--; + auto letter = m_data[m_length]; + m_data[m_length] = '\0'; + return letter; +} + +FudStatus String::pushBack(char letter) +{ + return pushBack(static_cast<uint8_t>(letter)); +} + +FudStatus String::pushBack(utf8 letter) +{ + if (!valid()) { + return FudStatus::StringInvalid; + } + + if (remainingLength() < 1) { + return FudStatus::OperationInvalid; + } + + m_data[m_length] = letter; + m_length++; + m_data[m_length] = '\0'; + + return FudStatus::Success; +} + +FudStatus String::pushBack(const FudUtf8& letter) +{ + if (!valid()) { + return FudStatus::StringInvalid; + } + + if (!letter.valid()) { + return FudStatus::InvalidInput; + } + + const auto* letterData = letter.data(); + if (letterData == nullptr) { + return FudStatus::InvalidInput; + } + + auto letterSize = letter.size(); + if (letterSize > remainingLength()) { + return FudStatus::OperationInvalid; + } + + auto copyStatus = copyMem(m_data + m_length, remainingLength(), letterData, letterSize); + + if (copyStatus != FudStatus::Success) { + return copyStatus; + } + + m_length += letterSize; + m_data[m_length] = '\0'; + + return FudStatus::Success; +} + +FudStatus String::catenate(StringView source) +{ + if (!valid()) { + return FudStatus::StringInvalid; + } + + if (source.data() == m_data) { + return FudStatus::Aliased; + } + + const auto newLength = m_length + source.length(); + const auto newSize = newLength + 1; + if (newSize >= m_capacity) { + return FudStatus::OperationInvalid; + } + + auto* destPtr = m_data + m_length; + auto status = copyMem(destPtr, m_capacity, source.data(), source.length()); + if (status == FudStatus::Success) { // likely + m_length += source.length(); + status = nullTerminate(); + } + + return status; +} + +String String::append(const String& rhs) const +{ + String output{}; + output.m_length = 1; + output.m_capacity = 0; + + if (!valid()) { + return output; + } + + output.m_length = m_length + rhs.length(); + output.m_capacity = output.m_length + 1; + if (output.isLarge()) { + output.m_data = static_cast<utf8*>(fudAlloc(output.m_capacity)); + } + + auto* destPtr = output.data(); + auto status = copyMem(destPtr, m_capacity, rhs.data(), rhs.length()); + assert(output.nullTerminate() == FudStatus::Success); + + return output; +} + +bool StringView::nullTerminated() const +{ + return m_data != nullptr && m_data[m_length] == '\0'; +} + +bool StringView::utf8Valid() const +{ + if (m_data == nullptr) { + return false; + } + + for (size_t idx = 0; idx < m_length;) { + if (Ascii::valid(m_data[idx])) { + idx++; + } else if (idx + 1 < m_length && Utf82Byte::valid(m_data[idx], m_data[idx + 1])) { + idx += 2; + } else if (idx + 2 < m_length && Utf83Byte::valid(m_data[idx], m_data[idx + 1], m_data[idx + 2])) { + idx += 3; + } else if ( + idx + 3 < m_length && Utf84Byte::valid(m_data[idx], m_data[idx + 1], m_data[idx + 2], m_data[idx + 3])) { + idx += 4; + } else { + return false; + } + } + + return true; +} + +Result<size_t, FudStatus> StringView::skipWhitespace() +{ + using RetType = Result<size_t, FudStatus>; + if (m_data == nullptr) { + return RetType::error(FudStatus::NullPointer); + } + size_t index = 0; + while (m_length > 0 && char_is_space(static_cast<char>(m_data[0]))) { + m_data++; + m_length--; + index++; + } + + return RetType::okay(index); +} + +Result<size_t, FudStatus> StringView::trimWhitespace() +{ + using RetType = Result<size_t, FudStatus>; + if (m_data == nullptr) { + return RetType::error(FudStatus::NullPointer); + } + + size_t count = 0; + while (m_length > 0 && char_is_space(static_cast<char>(m_data[m_length - 1]))) { + m_length--; + count++; + } + + return RetType::okay(count); +} + +#if 0 +FudStatus 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) +{ + if (source == nullptr) { + return FudStatus::NullPointer; + } + StringBorrow wrapper{*source}; + if (!wrapper.valid()) { + return FudStatus::StringInvalid; + } + + if ((newLength > 0 && static_cast<size_t>(newLength) > source->m_length) || + (static_cast<size_t>(-newLength) > source->m_length)) { + return FudStatus::InvalidInput; + } + + if (newLength < 0) { + source->m_length = source->m_length - static_cast<size_t>(-newLength); + } else { + source->m_length = static_cast<size_t>(newLength); + } + + return wrapper.nullTerminate(); +} + +FudStatus ext_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}); +} + +FudStatus ext_string_reverse_substring(ExtBasicString* source, StringView subString) +{ + auto dataOffset = subString.data - source->m_data; + if (dataOffset < 0 || static_cast<size_t>(dataOffset) > source->m_length) { + return FudStatus::InvalidInput; + } + if (static_cast<size_t>(dataOffset) + subString.length > source->m_length) { + return FudStatus::InvalidInput; + } + + if (source == nullptr || source->m_data == nullptr) { + return FudStatus::NullPointer; + } + + StringView view{subString}; + + size_t index = 0; + auto* data = source->m_data + dataOffset; + while (index < subString.length) { + if (ext_lib_char_is_ascii(static_cast<char>(data[index]))) { + index++; + continue; + } + auto utf8 = FudUtf8::fromStringView(view, index); + if (!utf8.valid()) { + return ExtUtf8Invalid; + } + const auto* utf8Data = utf8.data(); + if (utf8Data == nullptr) { + return ExtFailure; + } + auto utf8Size = utf8.size(); + switch (utf8Size) { + case 2: + data[index] = utf8Data[1]; + data[index + 1] = utf8Data[0]; + break; + case 3: + data[index] = utf8Data[2]; + data[index + 2] = utf8Data[0]; + break; + case 4: + data[index] = utf8Data[3]; + data[index + 1] = utf8Data[2]; + data[index + 2] = utf8Data[1]; + data[index + 3] = utf8Data[0]; + break; + default: + return ExtFailure; + } + index += utf8Size; + } + + ext_lib::DataView<uint8_t> dataView{subString.length, data}; + reverse(dataView); + + return FudStatus::Success; +} + +FudStatus ext_string_compare(StringView levo, StringView dextro, int* difference) +{ + if (anyAreNull(difference, levo.data, dextro.data)) { + return FudStatus::NullPointer; + } + + int diff = 0; + size_t index = 0; + while (diff == 0 && index < levo.length && index < dextro.length) { + diff = levo.data[index] - dextro.data[index]; + index++; + } + + if (diff != 0 || levo.length == dextro.length) { + /* nothing to do */ + } else if (levo.length > dextro.length) { + diff = static_cast<int>(levo.data[index]); + } else { + diff = -static_cast<int>(dextro.data[index]); + } + + *difference = diff; + return FudStatus::Success; +} + +FudStatus ext_string_chr(StringView extStringView, char character, size_t* index) +{ + if (anyAreNull(extStringView.data, index)) { + return FudStatus::NullPointer; + } + + bool found = false; + for (size_t localIndex = 0; localIndex < extStringView.length; ++localIndex) { + if (extStringView.data[localIndex] == static_cast<uint8_t>(character)) { + *index = localIndex; + found = true; + break; + } + } + + if (found) { + return FudStatus::Success; + } + + return ExtNotFound; +} + +FudStatus ext_string_unicode_chr(StringView extString, const ExtUtf8* unicode, size_t* index) +{ + if (anyAreNull(extString.data, unicode, index)) { + return FudStatus::NullPointer; + } + + if (!unicode->valid()) { + return ExtUtf8Invalid; + } + + size_t charSize = unicode->size(); + ExtDebugAssert(charSize != 0); + const uint8_t* dataMem = unicode->data(); + ExtDebugAssert(dataMem != nullptr); + + std::array<uint8_t, 4> localData{}; + auto copyStatus = ExtCopyMem(localData.data(), localData.size(), dataMem, charSize); + ExtDebugAssert(copyStatus == FudStatus::Success); + + for (size_t sIdx = 0; sIdx + charSize - 1 < extString.length;) { + + auto localChar = FudUtf8::fromStringView(extString, sIdx); + + if (!localChar.valid()) { + return ExtUtf8Invalid; + } + + if (localChar.m_variant == unicode->m_variant) { + *index = sIdx; + return FudStatus::Success; + } + + sIdx += localChar.size(); + } + + return ExtNotFound; +} + +namespace ext_lib { + +FudStatus ext_string_span_c_api( + const StringView& inputView, + const StringView& characterSetString, + StringView& result, + bool inSet) +{ + size_t firstIndex = inputView.length; + + size_t sIdx = 0; + while (sIdx < firstIndex) { + auto stringChar = FudUtf8::fromStringView(inputView, sIdx); + if (!stringChar.valid()) { + return ExtUtf8Invalid; + } + + size_t cIdx = 0; + bool found = false; + while (firstIndex > 0 && cIdx < firstIndex && cIdx < characterSetString.length) { + auto setChar = FudUtf8::fromStringView(characterSetString, cIdx); + if (!setChar.valid()) { + return ExtUtf8Invalid; + } + + if (stringChar == setChar) { + found = true; + } + + cIdx += setChar.size(); + } + + if (!inSet && found) { + firstIndex = sIdx; + } else if (inSet && !found) { + if (sIdx > 0) { + firstIndex = sIdx; + } + break; + } + + sIdx += stringChar.size(); + } + + if (firstIndex < inputView.length) { + result.length = inputView.length - firstIndex; + result.data = inputView.data + firstIndex; + return FudStatus::Success; + } + + return ExtNotFound; +} + +FudStatus ext_string_span_set(StringView inputView, const ExtUtf8Set* characterSet, StringView* stringView, bool inSet) +{ + if (anyAreNull(inputView.data, characterSet, stringView)) { + return FudStatus::NullPointer; + } + + if (!characterSet->valid()) { + return ExtUtf8Invalid; + } + + size_t firstIndex = inputView.length; + size_t sIdx = 0; + while (sIdx < firstIndex) { + auto localChar = FudUtf8::fromStringView(inputView, sIdx); + if (!localChar.valid()) { + return ExtUtf8Invalid; + } + + bool found = characterSet->contains(localChar); + + if (!inSet && found) { + firstIndex = sIdx; + } else if (inSet && !found) { + if (sIdx > 0) { + firstIndex = sIdx; + } + break; + } + + sIdx += localChar.size(); + } + + if (firstIndex < inputView.length) { + stringView->length = inputView.length - firstIndex; + stringView->data = inputView.data + firstIndex; + return FudStatus::Success; + } + + return ExtNotFound; +} + +} // namespace ext_lib + +FudStatus ext_string_span(StringView extString, StringView characterSetString, StringView* result) +{ + if (result == nullptr) { + return FudStatus::NullPointer; + } + + const StringView inputView{extString}; + const StringView characterSet{characterSetString}; + + return ext_string_span_c_api(inputView, characterSet, *result, true); +} + +FudStatus ext_string_c_span(StringView extString, StringView characterSetString, StringView* result) +{ + if (result == nullptr) { + return FudStatus::NullPointer; + } + + const StringView inputView{extString}; + const StringView characterSet{characterSetString}; + + return ext_string_span_c_api(inputView, characterSet, *result, false); +} + +FudStatus ext_string_span_set(StringView extString, const ExtUtf8Set* characterSet, StringView* stringView) +{ + return ext_lib::ext_string_span_set(extString, characterSet, stringView, true); +} + +FudStatus ext_string_c_span_set(StringView extString, const ExtUtf8Set* characterSet, StringView* stringView) +{ + return ext_lib::ext_string_span_set(extString, characterSet, stringView, false); +} + +FudStatus ext_string_find_substring(StringView haystack, StringView needle, StringView* stringView) +{ + if (anyAreNull(haystack.data, needle.data, stringView)) { + return FudStatus::NullPointer; + } + + if (needle.length > haystack.length) { + return ExtNotFound; + } + + if (needle.length == 1) { + size_t index = 0; + auto chrFindStatus = ext_string_chr(haystack, static_cast<char>(needle.data[0]), &index); + if (chrFindStatus == FudStatus::Success) { + stringView->data = haystack.data + index; + stringView->length = 1; + } + return chrFindStatus; + } + + size_t haystackIdx = 0; + while (haystackIdx < haystack.length - needle.length) { + StringView lhs; + lhs.data = haystack.data + haystackIdx; + lhs.length = haystack.length - haystackIdx; + size_t lhsIndex = 0; + auto chrFindStatus = ext_string_chr(lhs, static_cast<char>(needle.data[0]), &lhsIndex); + if (chrFindStatus != FudStatus::Success) { + return chrFindStatus; + } + haystackIdx += lhsIndex; + // GE or GT? + if (haystackIdx + needle.length >= haystack.length) { + break; + } + lhs.data = haystack.data + haystackIdx; + lhs.length = needle.length; + + int difference = -1; + auto cmpStatus = ext_string_compare(lhs, needle, &difference); + ExtDebugAssert(cmpStatus == FudStatus::Success); + if (difference == 0) { + stringView->data = lhs.data; + stringView->length = lhs.length; + return FudStatus::Success; + } + haystackIdx++; + } + + return ExtNotFound; +} + +namespace ext_lib { + +FudStatus skipWhitespace(StringView& view, size_t& skipIndex) +{ + auto skipResult = view.skipWhitespace(); + if (skipResult.isError()) { + return skipResult.getError(); + } + skipIndex = skipResult.getOkay(); + if (view.length < 1) { + return FudStatus::InvalidInput; + } + return FudStatus::Success; +} + +} // namespace ext_lib + +FudStatus ext_string_view_skip_whitespace(StringView* view) +{ + if (view == nullptr) { + return FudStatus::NullPointer; + } + + StringView sView{*view}; + auto skipResult = sView.skipWhitespace(); + if (skipResult.isError()) { + return skipResult.getError(); + } + view->data = sView.data; + view->length = sView.length; + return FudStatus::Success; +} + +FudStatus ext_string_view_trim_whitespace(StringView* view) +{ + if (view == nullptr) { + return FudStatus::NullPointer; + } + + StringView sView{*view}; + auto skipResult = sView.trimWhitespace(); + if (skipResult.isError()) { + return skipResult.getError(); + } + view->data = sView.data; + view->length = sView.length; + return FudStatus::Success; +} + +namespace impl { +constexpr ext_lib::Array<int8_t, 256> AsciiLookup{ + {-1, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, + -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + -2, -2, -2, -2, -2, -2, -2, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, -2, -2, -2, -2, -2, -2, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, + 29, 30, 31, 32, 33, 34, 35, -2, -2, -2, -2, -2, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, + -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, + -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, + -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, + -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3}}; + +// NOLINTBEGIN(readability-magic-numbers) +static_assert(AsciiLookup[static_cast<size_t>('0')] == 0); +static_assert(AsciiLookup[static_cast<size_t>('9')] == 9); +static_assert(AsciiLookup[static_cast<size_t>('a')] == 10); +static_assert(AsciiLookup[static_cast<size_t>('A')] == 10); +static_assert(AsciiLookup[static_cast<size_t>('f')] == 15); +static_assert(AsciiLookup[static_cast<size_t>('F')] == 15); +static_assert(AsciiLookup[127] == -2); +static_assert(AsciiLookup[128] == -3); +static_assert(AsciiLookup[255] == -3); +// NOLINTEND(readability-magic-numbers) + +FudStatus determineRadix(StringView input, uint8_t& radix, size_t& index) +{ + if (input.length < 1) { + return FudStatus::InvalidInput; + } + + if (input.length == 1 && input.data[0] == '0') { + radix = ExtRadixOctal; + return FudStatus::Success; + } + + if (input.length == 1) { + radix = ExtRadixDecimal; + return FudStatus::Success; + } + + if (input.data[0] == '0' && (input.data[1] == 'x' || input.data[1] == 'X')) { + radix = ExtRadixHexadecimal; + index += 2; + return FudStatus::Success; + } + + if (input.data[0] == '0') { + auto nextChar = input.data[1]; + auto nextVal = AsciiLookup[nextChar]; + if (nextVal >= 0 && nextVal < ExtRadixOctal) { + radix = ExtRadixOctal; + return FudStatus::Success; + } + if (nextVal >= ExtRadixOctal) { + return FudStatus::InvalidInput; + } + } + + radix = ExtRadixDecimal; + return FudStatus::Success; +} + +FudStatus getRadix(StringView& view, uint8_t& radix, size_t& skipIndex) +{ + if (radix == 0) { + size_t radixIndex = 0; + auto status = determineRadix(view, radix, radixIndex); + if (status != FudStatus::Success) { + return status; + } + skipIndex += radixIndex; + view.data += radixIndex; + view.length -= radixIndex; + } else if (radix == ExtRadixHexadecimal && view.length > 2 && (view.data[1] == 'x' || view.data[1] == 'X')) { + skipIndex += 2; + view.data += 2; + view.length -= 2; + } + return FudStatus::Success; +} + +FudStatus checkNegative(StringView& view, bool& isNegative, size_t& skipIndex) +{ + isNegative = view.data[0] == '-'; + if (isNegative && view.length == 1) { + return FudStatus::InvalidInput; + } + if (isNegative) { + skipIndex += 1; + view.data++; + view.length--; + } + return FudStatus::Success; +} + +FudStatus checkPlusSigned(StringView& view, size_t& skipIndex) +{ + auto isPlusSigned = view.data[0] == '+'; + if (isPlusSigned && view.length == 1) { + return FudStatus::InvalidInput; + } + if (isPlusSigned) { + skipIndex += 1; + view.data++; + view.length--; + } + return FudStatus::Success; +} + +template <typename T> +FudStatus stringViewToUnsignedInteger(StringView input, T& number, uint8_t specifiedRadix, size_t& index) +{ + if (input.data == nullptr) { + return FudStatus::NullPointer; + } + + if (specifiedRadix == 1 || specifiedRadix > ExtMaxRadix || input.length < 1) { + return FudStatus::InvalidInput; + } + + uint8_t radix = specifiedRadix; + + StringView view{input}; + size_t skipIndex = 0; + auto status = ext_lib::skipWhitespace(view, skipIndex); + if (status != FudStatus::Success) { + return status; + } + + status = checkPlusSigned(view, skipIndex); + if (status != FudStatus::Success) { + return FudStatus::InvalidInput; + } + + status = getRadix(view, radix, skipIndex); + + T num = 0; + size_t digitIndex = 0; + + while (digitIndex < view.length) { + auto digitResult = impl::AsciiLookup[view.data[digitIndex]]; + if (digitResult >= radix || digitResult < 0) { + break; + } + + auto digit = static_cast<uint8_t>(digitResult); + if (std::numeric_limits<T>::max() / radix < num) { + return FudStatus::InvalidInput; + } + num *= radix; + if (std::numeric_limits<T>::max() - digit < num) { + return FudStatus::InvalidInput; + } + num += digit; + digitIndex++; + } + if (digitIndex < 1) { + return FudStatus::InvalidInput; + } + + index = skipIndex + digitIndex; + number = num; + + return FudStatus::Success; +} + +template <typename T> +FudStatus stringViewToUnsignedInteger(StringView input, T* number, uint8_t specifiedRadix, size_t* index) +{ + if (anyAreNull(input.data, number)) { + return FudStatus::NullPointer; + } + + size_t localIndex = 0; + + auto status = stringViewToUnsignedInteger(input, *number, specifiedRadix, localIndex); + if (status == FudStatus::Success && index != nullptr) { + *index = localIndex; + } + return status; +} + +template <typename T> +FudStatus viewToSignedIntPositive(StringView view, uint8_t radix, size_t& digitIndex, T& num) +{ + digitIndex = 0; + while (digitIndex < view.length) { + int8_t digitResult = impl::AsciiLookup[view.data[digitIndex]]; + if (digitResult >= radix) { + return FudStatus::InvalidInput; + } + if (digitResult < 0) { + break; + } + auto digit = static_cast<uint8_t>(digitResult); + if (std::numeric_limits<T>::max() / radix < num) { + return FudStatus::InvalidInput; + } + num = static_cast<T>(num * radix); + if (std::numeric_limits<T>::max() - digit < num) { + return FudStatus::InvalidInput; + } + num = static_cast<T>(num + digit); + digitIndex++; + } + + return FudStatus::Success; +} + +template <typename T> +FudStatus viewToSignedIntNegative(StringView view, uint8_t radix, size_t& digitIndex, T& num) +{ + digitIndex = 0; + while (digitIndex < view.length) { + int8_t digitResult = impl::AsciiLookup[view.data[digitIndex]]; + if (digitResult >= radix) { + return FudStatus::InvalidInput; + } + if (digitResult < 0) { + break; + } + auto digit = static_cast<uint8_t>(digitResult); + if ((std::numeric_limits<T>::min() / radix > num)) { + return FudStatus::InvalidInput; + } + num = static_cast<T>(num * radix); + if (std::numeric_limits<T>::min() + digit > num) { + return FudStatus::InvalidInput; + } + num = static_cast<T>(num - digit); + digitIndex++; + } + + return FudStatus::Success; +} + +template <typename T> +FudStatus stringViewToSignedInteger(StringView input, T& number, uint8_t specifiedRadix, size_t& index) +{ + if (input.data == nullptr) { + return FudStatus::NullPointer; + } + + auto radix = specifiedRadix; + + StringView view{input}; + size_t skipIndex = 0; + auto status = ext_lib::skipWhitespace(view, skipIndex); + if (status != FudStatus::Success) { + return status; + } + + bool isNegative = false; + status = checkNegative(view, isNegative, skipIndex); + if (status != FudStatus::Success) { + return FudStatus::InvalidInput; + } + + if (!isNegative) { + status = checkPlusSigned(view, skipIndex); + if (status != FudStatus::Success) { + return FudStatus::InvalidInput; + } + } + + status = getRadix(view, radix, skipIndex); + + T num = 0; + size_t digitIndex = 0; + + if (isNegative) { + status = viewToSignedIntNegative(view, radix, digitIndex, num); + } else { + status = viewToSignedIntPositive(view, radix, digitIndex, num); + } + if (status != FudStatus::Success) { + return status; + } + + if (digitIndex < 1) { + return FudStatus::InvalidInput; + } + + index = skipIndex + digitIndex; + number = num; + return FudStatus::Success; +} + +template <typename T> +FudStatus stringViewToSignedInteger(StringView input, T* number, uint8_t specifiedRadix, size_t* index) +{ + if (anyAreNull(input.data, number)) { + return FudStatus::NullPointer; + } + + if (specifiedRadix == 1 || specifiedRadix > ExtMaxRadix || input.length < 1) { + return FudStatus::InvalidInput; + } + + size_t localIndex = 0; + auto status = stringViewToSignedInteger(input, *number, specifiedRadix, localIndex); + if (status == FudStatus::Success && index != nullptr) { + *index = localIndex; + } + return status; +} + +} // namespace impl + +FudStatus ext_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) +{ + return impl::stringViewToUnsignedInteger(input, number, specifiedRadix, index); +} + +FudStatus ext_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) +{ + return impl::stringViewToUnsignedInteger(input, number, specifiedRadix, index); +} + +FudStatus ext_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) +{ + return impl::stringViewToSignedInteger(input, number, specifiedRadix, index); +} + +FudStatus ext_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) +{ + return impl::stringViewToSignedInteger(input, number, specifiedRadix, index); +} + +namespace impl { + +template <typename T> +bool isNanOrInf(T& num, StringView& view, T& sign, size_t& digitIndex) +{ + if (view.length >= 3) { + std::array<uint8_t, 3> letters{{view.data[0], view.data[1], view.data[2]}}; + ext_lib::mapMut(letters, ext_lib_char_to_lower); + if (letters[0] == 'i' && letters[1] == 'n' && letters[2] == 'f') { + num = sign * std::numeric_limits<T>::infinity(); + digitIndex = 3; + return true; + } + if (letters[0] == 'n' && letters[1] == 'a' && letters[2] == 'n') { + num = std::numeric_limits<T>::quiet_NaN(); + ; + digitIndex = 3; + return true; + } + } + return false; +} + +template <typename T> +FudStatus getWhole( + const StringView view, + size_t& digitIndex, + T& num, + T sign, + uint8_t radix, + bool& foundDecimal, + bool& foundExponent) +{ + while (digitIndex < view.length) { + auto nextChar = view.data[digitIndex]; + if (nextChar == '.') { + foundDecimal = true; + digitIndex++; + break; + } + + if (radix == ExtRadixDecimal && (nextChar == 'e' || nextChar == 'E')) { + foundExponent = true; + digitIndex++; + break; + } + + auto digitResult = impl::AsciiLookup[nextChar]; + if (digitResult >= radix) { + return FudStatus::InvalidInput; + } + if (digitResult < 0) { + break; + } + auto digit = static_cast<T>(digitResult) * sign; + num *= static_cast<T>(radix); + + num += digit; + digitIndex++; + } + return FudStatus::Success; +} + +template <typename T> +FudStatus getExponent(const StringView& view, size_t& digitIndex, T& num, uint8_t radix) +{ + int32_t exponent{}; + StringView tempView{view.length - digitIndex, view.data + digitIndex}; + size_t exponentLength{}; + auto status = tempView.toInt32(exponent, ExtRadixDecimal, exponentLength); + if (status != FudStatus::Success) { + return status; + } + digitIndex += exponentLength; + num = num * std::pow(static_cast<T>(radix), static_cast<T>(exponent)); + return FudStatus::Success; +} + +template <typename T> +FudStatus getFraction(const StringView view, size_t& digitIndex, T& num, T sign, uint8_t radix, bool& foundExponent) +{ + auto radixDiv = 1.0F / static_cast<T>(radix); + while (digitIndex < view.length) { + auto nextChar = view.data[digitIndex]; + if (radix == ExtRadixDecimal && (nextChar == 'e' || nextChar == 'E')) { + foundExponent = true; + digitIndex++; + break; + } + + auto digitResult = impl::AsciiLookup[nextChar]; + if (digitResult >= radix) { + return FudStatus::InvalidInput; + } + if (digitResult < 0) { + break; + } + auto digit = static_cast<T>(digitResult) * sign; + num += digit * radixDiv; + radixDiv /= static_cast<T>(radix); + digitIndex++; + } + return FudStatus::Success; +} + +template <typename T> +FudStatus stringViewToFloat(StringView input, T& number, size_t& index) +{ + if (input.data == nullptr) { + return FudStatus::NullPointer; + } + + if (input.length < 1) { + return FudStatus::InvalidInput; + } + + uint8_t radix = 0; + + StringView view{input}; + size_t skipIndex = 0; + + auto status = ext_lib::skipWhitespace(view, skipIndex); + if (status != FudStatus::Success) { + return status; + } + + T sign = 1.0; + bool isNegative = false; + status = impl::checkNegative(view, isNegative, skipIndex); + if (status != FudStatus::Success) { + return FudStatus::InvalidInput; + } + + if (!isNegative) { + status = checkPlusSigned(view, skipIndex); + } else { + sign = -1.0; + } + + if (status != FudStatus::Success) { + return FudStatus::InvalidInput; + } + + T num = 0; + size_t digitIndex = 0; + + auto retSuccess = [&]() { + index = skipIndex + digitIndex; + number = num; + return FudStatus::Success; + }; + + if (impl::isNanOrInf(num, view, sign, digitIndex)) { + return retSuccess(); + } + + status = impl::getRadix(view, radix, skipIndex); + if (status != FudStatus::Success) { + return status; + } + + bool foundDecimal = false; + bool foundExponent = false; + status = getWhole(view, digitIndex, num, sign, radix, foundDecimal, foundExponent); + + if (status == FudStatus::Success && foundExponent) { + status = getExponent(view, digitIndex, num, radix); + } + + if (status != FudStatus::Success) { + return status; + } + + if (!foundDecimal) { + if (digitIndex < 1) { + return FudStatus::InvalidInput; + } + + return retSuccess(); + } + + status = getFraction(view, digitIndex, num, sign, radix, foundExponent); + + if (foundExponent) { + status = getExponent(view, digitIndex, num, radix); + if (status != FudStatus::Success) { + return status; + } + } + + if (digitIndex < 1) { + return FudStatus::InvalidInput; + } + + if (std::isinf(num) || std::isnan(num)) // isnan is dubious here - likely unreachable + { + return ExtRangeError; + } + + return retSuccess(); +} + +template <typename T> +FudStatus stringViewToFloat(StringView input, T* number, size_t* index) +{ + if (anyAreNull(input.data, number)) { + return FudStatus::NullPointer; + } + + size_t localIndex{0}; + auto status = stringViewToFloat(input, *number, localIndex); + + if (status == FudStatus::Success && index != nullptr) { + *index = localIndex; + } + return status; +} + +} // namespace impl + +FudStatus ext_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) +{ + return impl::stringViewToFloat(input, number, index); +} + +namespace ext_lib { + +FudStatus StringView::toUint8(uint8_t& number, uint8_t specifiedRadix, size_t& strLen) const +{ + return ::impl::stringViewToUnsignedInteger(*this, number, specifiedRadix, strLen); +} + +FudStatus StringView::toUint16(uint16_t& number, uint8_t specifiedRadix, size_t& strLen) const +{ + return ::impl::stringViewToUnsignedInteger(*this, number, specifiedRadix, strLen); +} + +FudStatus StringView::toUint32(uint32_t& number, uint8_t specifiedRadix, size_t& strLen) const +{ + return ::impl::stringViewToUnsignedInteger(*this, number, specifiedRadix, strLen); +} + +FudStatus StringView::toUint64(uint64_t& number, uint8_t specifiedRadix, size_t& strLen) const +{ + return ::impl::stringViewToUnsignedInteger(*this, number, specifiedRadix, strLen); +} + +FudStatus StringView::toInt8(int8_t& number, uint8_t specifiedRadix, size_t& strLen) const +{ + return ::impl::stringViewToSignedInteger(*this, number, specifiedRadix, strLen); +} + +FudStatus StringView::toInt16(int16_t& number, uint8_t specifiedRadix, size_t& strLen) const +{ + return ::impl::stringViewToSignedInteger(*this, number, specifiedRadix, strLen); +} + +FudStatus StringView::toInt32(int32_t& number, uint8_t specifiedRadix, size_t& strLen) const +{ + return ::impl::stringViewToSignedInteger(*this, number, specifiedRadix, strLen); +} + +FudStatus StringView::toInt64(int64_t& number, uint8_t specifiedRadix, size_t& strLen) const +{ + return ::impl::stringViewToSignedInteger(*this, number, specifiedRadix, strLen); +} + +FudStatus StringView::toFloat(float& number, size_t& strLen) const +{ + return ::impl::stringViewToFloat(*this, number, strLen); +} + +FudStatus StringView::toDouble(double& number, size_t& strLen) const +{ + return ::impl::stringViewToFloat(*this, number, strLen); +} + +#endif + +} // namespace fud diff --git a/source/utf8.cpp b/source/fud_utf8.cpp index c94ac1f..5dd5099 100644 --- a/source/utf8.cpp +++ b/source/fud_utf8.cpp @@ -15,15 +15,15 @@ * limitations under the License. */ -#include "utf8.hpp" +#include "fud_utf8.hpp" -#include "string.hpp" +#include "fud_string.hpp" #include <new> // IWYU pragma: keep - this is for placement new overloads. namespace fud { -ExtUtf8 ExtUtf8::fromString(const String& fudString, size_t index) noexcept +FudUtf8 FudUtf8::fromString(const String& fudString, size_t index) noexcept { if (!fudString.valid()) { return invalidAscii(); @@ -33,12 +33,12 @@ ExtUtf8 ExtUtf8::fromString(const String& fudString, size_t index) noexcept return fromStringView(StringView{fudString}, index); } -ExtUtf8 ExtUtf8::fromStringView(const StringView& view, size_t index) noexcept +FudUtf8 FudUtf8::fromStringView(const StringView& view, size_t index) noexcept { return fromStringView(StringView{view}, index); } -ExtUtf8 ExtUtf8::fromStringView(StringView&& view, size_t index) noexcept +FudUtf8 FudUtf8::fromStringView(StringView&& view, size_t index) noexcept { auto len = view.length(); const auto* data = view.data(); @@ -46,7 +46,7 @@ ExtUtf8 ExtUtf8::fromStringView(StringView&& view, size_t index) noexcept return invalidAscii(); } - ExtUtf8 localChar{Ascii{data[index]}}; + FudUtf8 localChar{Ascii{data[index]}}; if (localChar.valid()) { return localChar; } @@ -75,18 +75,18 @@ ExtUtf8 ExtUtf8::fromStringView(StringView&& view, size_t index) noexcept return invalidAscii(); } -bool ext_lib_char_is_ascii(char character) +bool char_is_ascii(char character) { return static_cast<uint8_t>(character & ~ASCII_MASK) == 0; } -FudStatus ext_lib_utf8_is_ascii(ExtUtf8* character, bool* isAscii) +FudStatus utf8_is_ascii(FudUtf8* character, bool* isAscii) { if (anyAreNull(character, isAscii)) { return FudStatus::NullPointer; } - *isAscii = character->getType() == ExtUtf8Type::Ascii && character->valid(); + *isAscii = character->getType() == Utf8Type::Ascii && character->valid(); return FudStatus::Success; } @@ -95,7 +95,7 @@ namespace impl { /* Assumes that predicate is not a null pointer! */ template <typename Predicate> -inline FudStatus isAsciiPredicate(ExtUtf8* character, bool* pred, Predicate&& predicate) +inline FudStatus isAsciiPredicate(FudUtf8* character, bool* pred, Predicate&& predicate) { if (anyAreNull(character, pred)) { return FudStatus::NullPointer; @@ -114,87 +114,87 @@ inline FudStatus isAsciiPredicate(ExtUtf8* character, bool* pred, Predicate&& pr } // namespace impl -bool ext_lib_char_is_alphanumeric(char character) +bool char_is_alphanumeric(char character) { - if (!ext_lib_char_is_ascii(character)) { + if (!char_is_ascii(character)) { return false; } - if (ext_lib_char_is_alpha(character)) { + if (char_is_alpha(character)) { return true; } - return ext_lib_char_is_digit(character); + return char_is_digit(character); } -FudStatus ext_lib_utf8_is_alphanumeric(ExtUtf8* character, bool* pred) +FudStatus utf8_is_alphanumeric(FudUtf8* character, bool* pred) { - return impl::isAsciiPredicate(character, pred, ext_lib_char_is_alphanumeric); + return impl::isAsciiPredicate(character, pred, char_is_alphanumeric); } -bool ext_lib_char_is_alpha(char character) +bool char_is_alpha(char character) { - if (!ext_lib_char_is_ascii(character)) { + if (!char_is_ascii(character)) { return false; } - if (ext_lib_char_is_uppercase(character)) { + if (char_is_uppercase(character)) { return true; } - return ext_lib_char_is_lowercase(character); + return char_is_lowercase(character); } -FudStatus ext_lib_utf8_is_alpha(ExtUtf8* character, bool* pred) +FudStatus utf8_is_alpha(FudUtf8* character, bool* pred) { - return impl::isAsciiPredicate(character, pred, ext_lib_char_is_alpha); + return impl::isAsciiPredicate(character, pred, char_is_alpha); } -bool ext_lib_char_is_lowercase(char character) +bool char_is_lowercase(char character) { - if (!ext_lib_char_is_ascii(character)) { + if (!char_is_ascii(character)) { return false; } return 'a' <= character && character <= 'z'; } -FudStatus ext_lib_utf8_is_lowercase(ExtUtf8* character, bool* pred) +FudStatus utf8_is_lowercase(FudUtf8* character, bool* pred) { - return impl::isAsciiPredicate(character, pred, ext_lib_char_is_lowercase); + return impl::isAsciiPredicate(character, pred, char_is_lowercase); } -bool ext_lib_char_is_uppercase(char character) +bool char_is_uppercase(char character) { - if (!ext_lib_char_is_ascii(character)) { + if (!char_is_ascii(character)) { return false; } return 'A' <= character && character <= 'Z'; } -FudStatus ext_lib_utf8_is_uppercase(ExtUtf8* character, bool* pred) +FudStatus utf8_is_uppercase(FudUtf8* character, bool* pred) { - return impl::isAsciiPredicate(character, pred, ext_lib_char_is_uppercase); + return impl::isAsciiPredicate(character, pred, char_is_uppercase); } -bool ext_lib_char_is_digit(char character) +bool char_is_digit(char character) { - if (!ext_lib_char_is_ascii(character)) { + if (!char_is_ascii(character)) { return false; } return '0' <= character && character <= '9'; } -FudStatus ext_lib_utf8_is_digit(ExtUtf8* character, bool* pred) +FudStatus utf8_is_digit(FudUtf8* character, bool* pred) { - return impl::isAsciiPredicate(character, pred, ext_lib_char_is_digit); + return impl::isAsciiPredicate(character, pred, char_is_digit); } -bool ext_lib_char_is_hex_digit(char character) +bool char_is_hex_digit(char character) { - if (!ext_lib_char_is_ascii(character)) { + if (!char_is_ascii(character)) { return false; } @@ -202,14 +202,14 @@ bool ext_lib_char_is_hex_digit(char character) ('A' <= character && character <= 'F'); } -FudStatus ext_lib_utf8_is_hex_digit(ExtUtf8* character, bool* pred) +FudStatus utf8_is_hex_digit(FudUtf8* character, bool* pred) { - return impl::isAsciiPredicate(character, pred, ext_lib_char_is_hex_digit); + return impl::isAsciiPredicate(character, pred, char_is_hex_digit); } -bool ext_lib_char_is_control(char character) +bool char_is_control(char character) { - if (!ext_lib_char_is_ascii(character)) { + if (!char_is_ascii(character)) { return false; } @@ -218,70 +218,70 @@ bool ext_lib_char_is_control(char character) return ((static_cast<uint8_t>(character) <= maxControlChar)) || character == deleteChar; } -FudStatus ext_lib_utf8_is_control(ExtUtf8* character, bool* pred) +FudStatus utf8_is_control(FudUtf8* character, bool* pred) { - return impl::isAsciiPredicate(character, pred, ext_lib_char_is_control); + return impl::isAsciiPredicate(character, pred, char_is_control); } -bool ext_lib_char_is_graphical(char character) +bool char_is_graphical(char character) { - if (!ext_lib_char_is_ascii(character)) { + if (!char_is_ascii(character)) { return false; } - return ext_lib_char_is_alphanumeric(character) || ext_lib_char_is_punctuation(character); + return char_is_alphanumeric(character) || char_is_punctuation(character); } -FudStatus ext_lib_utf8_is_graphical(ExtUtf8* character, bool* pred) +FudStatus utf8_is_graphical(FudUtf8* character, bool* pred) { - return impl::isAsciiPredicate(character, pred, ext_lib_char_is_graphical); + return impl::isAsciiPredicate(character, pred, char_is_graphical); } -bool ext_lib_char_is_space(char character) +bool char_is_space(char character) { - if (!ext_lib_char_is_ascii(character)) { + if (!char_is_ascii(character)) { return false; } return character == ' ' || character == '\t' || character == '\n' || character == '\r' || character == '\v'; } -FudStatus ext_lib_utf8_is_space(ExtUtf8* character, bool* pred) +FudStatus utf8_is_space(FudUtf8* character, bool* pred) { - return impl::isAsciiPredicate(character, pred, ext_lib_char_is_space); + return impl::isAsciiPredicate(character, pred, char_is_space); } -bool ext_lib_char_is_blank(char character) +bool char_is_blank(char character) { - if (!ext_lib_char_is_ascii(character)) { + if (!char_is_ascii(character)) { return false; } return character == ' ' || character == '\t'; } -FudStatus ext_lib_utf8_is_blank(ExtUtf8* character, bool* pred) +FudStatus utf8_is_blank(FudUtf8* character, bool* pred) { - return impl::isAsciiPredicate(character, pred, ext_lib_char_is_blank); + return impl::isAsciiPredicate(character, pred, char_is_blank); } -bool ext_lib_char_is_printable(char character) +bool char_is_printable(char character) { - if (!ext_lib_char_is_ascii(character)) { + if (!char_is_ascii(character)) { return false; } return (character >= ' ' && character <= '~'); } -FudStatus ext_lib_utf8_is_printable(ExtUtf8* character, bool* pred) +FudStatus utf8_is_printable(FudUtf8* character, bool* pred) { - return impl::isAsciiPredicate(character, pred, ext_lib_char_is_printable); + return impl::isAsciiPredicate(character, pred, char_is_printable); } -bool ext_lib_char_is_punctuation(char character) +bool char_is_punctuation(char character) { - if (!ext_lib_char_is_ascii(character)) { + if (!char_is_ascii(character)) { return false; } @@ -289,14 +289,14 @@ bool ext_lib_char_is_punctuation(char character) (character >= '[' && character <= '`') || (character >= '{' && character <= '~'); } -FudStatus ext_lib_utf8_is_punctuation(ExtUtf8* character, bool* pred) +FudStatus utf8_is_punctuation(FudUtf8* character, bool* pred) { - return impl::isAsciiPredicate(character, pred, ext_lib_char_is_punctuation); + return impl::isAsciiPredicate(character, pred, char_is_punctuation); } -uint8_t ext_lib_char_to_lower(uint8_t character) +uint8_t char_to_lower(uint8_t character) { - if (ext_lib_char_is_uppercase(static_cast<char>(character))) { + if (char_is_uppercase(static_cast<char>(character))) { constexpr uint8_t lowerA = 'a'; constexpr uint8_t upperA = 'A'; return static_cast<uint8_t>(character - upperA) + lowerA; @@ -304,22 +304,22 @@ uint8_t ext_lib_char_to_lower(uint8_t character) return character; } -ExtUtf8* ext_lib_utf8_to_lower(ExtUtf8* character) +FudUtf8* utf8_to_lower(FudUtf8* character) { if (character == nullptr) { return character; } static_cast<void>(character->transformAscii([](Ascii& ascii) { - ascii = Ascii{ext_lib_char_to_lower(static_cast<uint8_t>(ascii.asChar()))}; + ascii = Ascii{char_to_lower(static_cast<uint8_t>(ascii.asChar()))}; })); return character; } -uint8_t ext_lib_char_to_upper(uint8_t character) +uint8_t char_to_upper(uint8_t character) { - if (ext_lib_char_is_lowercase(static_cast<char>(character))) { + if (char_is_lowercase(static_cast<char>(character))) { constexpr uint8_t lowerA = 'a'; constexpr uint8_t upperA = 'A'; return static_cast<uint8_t>(character - lowerA) + upperA; @@ -327,17 +327,17 @@ uint8_t ext_lib_char_to_upper(uint8_t character) return character; } -ExtUtf8* ext_lib_utf8_to_upper(ExtUtf8* character) +FudUtf8* utf8_to_upper(FudUtf8* character) { if (character == nullptr) { return character; } static_cast<void>(character->transformAscii([](Ascii& ascii) { - ascii = Ascii{ext_lib_char_to_upper(static_cast<uint8_t>(ascii.asChar()))}; + ascii = Ascii{char_to_upper(static_cast<uint8_t>(ascii.asChar()))}; })); return character; } -} // namespace ext_lib +} // namespace fud diff --git a/source/fud_utf8_iterator.cpp b/source/fud_utf8_iterator.cpp new file mode 100644 index 0000000..4476050 --- /dev/null +++ b/source/fud_utf8_iterator.cpp @@ -0,0 +1,55 @@ +/* + * 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_utf8_iterator.hpp" + +namespace fud { + +std::optional<FudUtf8> Utf8Iterator::peek() const +{ + if (m_index >= m_view.length()) { + return std::nullopt; + } + + auto utf8 = FudUtf8::fromStringView(m_view, m_index); + + if (!utf8.valid()) { + return std::nullopt; + } + + return utf8; +} + +std::optional<FudUtf8> Utf8Iterator::next() +{ + if (m_index >= m_view.length()) { + m_index = m_view.length(); + return std::nullopt; + } + + auto utf8 = FudUtf8::fromStringView(m_view, m_index); + + if (!utf8.valid()) { + m_index = m_view.length(); + return std::nullopt; + } + + m_index += utf8.size(); + return utf8; +} + +} // namespace fud diff --git a/source/libfud.cpp b/source/libfud.cpp index fa0e3a0..834082e 100644 --- a/source/libfud.cpp +++ b/source/libfud.cpp @@ -1,4 +1,5 @@ /* + * libfud * Copyright 2024 Dominick Allen * * Licensed under the Apache License, Version 2.0 (the "License"); you @@ -18,7 +19,8 @@ namespace fud { -void fud() { +void fud() +{ } } // namespace fud diff --git a/source/string.cpp b/source/string.cpp deleted file mode 100644 index a121418..0000000 --- a/source/string.cpp +++ /dev/null @@ -1,19 +0,0 @@ -/* - * 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 "string.hpp" diff --git a/source/utf8_iterator.cpp b/source/utf8_iterator.cpp deleted file mode 100644 index e439687..0000000 --- a/source/utf8_iterator.cpp +++ /dev/null @@ -1,38 +0,0 @@ -#include "utf8_iterator.hpp" - -namespace fud { - -std::optional<ExtUtf8> Utf8Iterator::peek() const -{ - if (m_index >= m_view.length()) { - return std::nullopt; - } - - auto utf8 = ExtUtf8::fromStringView(m_view, m_index); - - if (!utf8.valid()) { - return std::nullopt; - } - - return utf8; -} - -std::optional<ExtUtf8> Utf8Iterator::next() -{ - if (m_index >= m_view.length()) { - m_index = m_view.length(); - return std::nullopt; - } - - auto utf8 = ExtUtf8::fromStringView(m_view, m_index); - - if (!utf8.valid()) { - m_index = m_view.length(); - return std::nullopt; - } - - m_index += utf8.size(); - return utf8; -} - -} // namespace fud diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100644 index 0000000..9bd9e87 --- /dev/null +++ b/test/CMakeLists.txt @@ -0,0 +1,66 @@ +include(FetchContent) + +if(CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo") + set(CVG_FLAGS -Wno-long-long -fsanitize=address -fsanitize=undefined -fprofile-arcs -ftest-coverage) +else() + +endif() + +set(gtest_URL https://github.com/google/googletest.git) +set(gtest_TAG v1.14.0) + +FetchContent_Declare( + googletest + GIT_REPOSITORY ${gtest_URL} + GIT_TAG ${gtest_TAG} +) +FetchContent_MakeAvailable(googletest) +include(GoogleTest) + +enable_testing() + +set(CMAKE_THREAD_LIBS_INIT "-lpthread") +set(CMAKE_HAVE_THREADS_LIBRARY 1) +set(CMAKE_USE_WIN32_THREADS_INIT 0) +set(CMAKE_USE_PTHREADS_INIT 1) +set(THREADS_PREFER_PTHREAD_FLAG ON) + +function(fud_add_test test_name) + set(options NO_OPTIONS) + set(oneValueArgs NO_ONE_VALUE_ARGS) + set(multiValueArgs SOURCES) + cmake_parse_arguments(FUD_ADD_TEST "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + add_executable(${test_name} test_common.cpp ${FUD_ADD_TEST_SOURCES}) + + target_include_directories(${test_name} PUBLIC $<TARGET_PROPERTY:libfud>) + + target_link_libraries(${test_name} PUBLIC GTest::gtest_main libfud) + + target_compile_options(${test_name} PRIVATE ${CVG_FLAGS}) + target_link_options(${test_name} PRIVATE ${CVG_FLAGS}) + + set_target_properties( + ${test_name} PROPERTIES + CXX_STANDARD 20 + C_STANDARD 23 + CXX_EXTENSIONS OFF + C_EXTENSIONS OFF + CXX_STANDARD_REQUIRED ON) + + gtest_discover_tests(${test_name}) +endfunction() + +fud_add_test(test_result SOURCES test_result.cpp) +# fud_add_test(test_ext_algorithm SOURCES test_algorithm.cpp) +# fud_add_test(test_ext_array SOURCES +# test_ext_array.cpp +# test_ext_unique_array.cpp) +# fud_add_test(test_ext_utf8 SOURCES +# test_ext_utf8.cpp) +# fud_add_test(test_ext_string SOURCES +# test_ext_string.cpp +# test_ext_string_cxx.cpp) +# fud_add_test(test_ext_string_format SOURCES +# test_ext_string_format.cpp) + diff --git a/test/test_common.cpp b/test/test_common.cpp new file mode 100644 index 0000000..a03c8db --- /dev/null +++ b/test/test_common.cpp @@ -0,0 +1,18 @@ +#include "fud_memory.hpp" +#include <cstdlib> + +namespace fud { + +void* fudAlloc(size_t size) { + return malloc(size); +} + +void* fudRealloc(void* ptr, size_t size) { + return realloc(ptr, size); +} + +void fudFree(void* ptr) { + return free(ptr); +} + +} // namespace fud diff --git a/test/test_result.cpp b/test/test_result.cpp new file mode 100644 index 0000000..d86a170 --- /dev/null +++ b/test/test_result.cpp @@ -0,0 +1,52 @@ +/* + * ExtLib + * 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_status.hpp" +#include "fud_result.hpp" + +#include "gtest/gtest.h" + +namespace fud { + +using GResult = Result<uint, FudStatus>; + +TEST(ResultTest, OkResult) +{ + GResult ok_res = GResult::okay(1); + + ASSERT_TRUE(ok_res.isOkay()); + ASSERT_FALSE(ok_res.isError()); + ASSERT_EQ(ok_res.getOkay(), 1); +} + +TEST(ResultTest, ErrResult) +{ + GResult err_res = GResult::error(FudStatus::Failure); + + ASSERT_TRUE(err_res.isError()); + ASSERT_FALSE(err_res.isOkay()); + auto err = err_res.getError(); + + ASSERT_EQ(err, FudStatus::Failure); + + const auto status = FudStatus::InvalidInput; + GResult err2 = GResult::error(status); + ASSERT_TRUE(err2.isError()); + ASSERT_EQ(err2.getError(), FudStatus::InvalidInput); +} + +} // namespace fud |