diff options
-rw-r--r-- | CMakeLists.txt | 11 | ||||
-rw-r--r-- | cmake/CheckGit.cmake | 14 | ||||
-rw-r--r-- | cmake/fud_config.hpp.in (renamed from cmake/fud_version.hpp.in) | 3 | ||||
-rw-r--r-- | include/fud_algorithm.hpp | 36 | ||||
-rw-r--r-- | include/fud_array.hpp | 10 | ||||
-rw-r--r-- | include/fud_memory.hpp | 16 | ||||
-rw-r--r-- | include/fud_option.hpp | 229 | ||||
-rw-r--r-- | include/fud_span.hpp | 10 | ||||
-rw-r--r-- | include/fud_status.hpp | 66 | ||||
-rw-r--r-- | include/fud_vector.hpp | 628 | ||||
-rw-r--r-- | source/fud_string_view.cpp | 2 | ||||
-rw-r--r-- | source/libfud.cpp | 8 | ||||
-rw-r--r-- | test/CMakeLists.txt | 2 | ||||
-rw-r--r-- | test/test_common.hpp | 6 | ||||
-rw-r--r-- | test/test_fud.cpp | 27 | ||||
-rw-r--r-- | test/test_option.cpp | 54 | ||||
-rw-r--r-- | test/test_utf8.cpp | 1125 | ||||
-rw-r--r-- | test/test_vector.cpp | 138 |
18 files changed, 1488 insertions, 897 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index f0e9aff..5a41872 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -40,12 +40,17 @@ target_link_libraries(fud ${SQLite3_LIBRARIES}) set_target_properties( fud PROPERTIES - CXX_STANDARD 23 + CXX_STANDARD 20 C_STANDARD 23 CXX_EXTENSIONS OFF C_EXTENSIONS OFF CXX_STANDARD_REQUIRED ON) +if (DEFINED FUD_BOUNDS_CHECKING) +else() + set(FUD_BOUNDS_CHECKING true) +endif() + if (FUD_TEST) add_subdirectory(test) # set(CVG_FLAGS -fsanitize=address -fsanitize=undefined --coverage) @@ -98,6 +103,7 @@ set(FUD_HEADERS "include/fud_directory.hpp" "include/fud_fud_type_traits.hpp" "include/fud_memory.hpp" + "include/fud_option.hpp" "include/fud_permissions.hpp" "include/fud_result.hpp" "include/fud_span.hpp" @@ -109,6 +115,7 @@ set(FUD_HEADERS "include/fud_utf8.hpp" "include/fud_utf8_iterator.hpp" "include/fud_vector.hpp" + "${CMAKE_CURRENT_BINARY_DIR}/fud_config.hpp" ) set_target_properties(fud PROPERTIES PUBLIC_HEADER "${FUD_HEADERS}") @@ -121,5 +128,5 @@ install(TARGETS fud include(cmake/CheckGit.cmake) CheckGitSetup(GIT_HASH) -configure_file(cmake/fud_version.hpp.in include/fud_version.hpp @ONLY) +configure_file(cmake/fud_config.hpp.in include/fud_config.hpp @ONLY) add_dependencies(fud AlwaysCheckGit) diff --git a/cmake/CheckGit.cmake b/cmake/CheckGit.cmake index f7d8bce..46736e7 100644 --- a/cmake/CheckGit.cmake +++ b/cmake/CheckGit.cmake @@ -7,8 +7,8 @@ if (NOT DEFINED post_configure_dir) set(post_configure_dir ${CMAKE_BINARY_DIR}/include) endif () -set(pre_configure_file ${pre_configure_dir}/fud_version.hpp.in) -set(post_configure_file ${post_configure_dir}/fud_version.hpp) +set(pre_configure_file ${pre_configure_dir}/fud_config.hpp.in) +set(post_configure_file ${post_configure_dir}/fud_config.hpp) function(CheckGitWrite git_hash) file(WRITE ${CMAKE_BINARY_DIR}/git-state.txt ${git_hash}) @@ -37,15 +37,15 @@ function(CheckGitVersion git_hash) file(MAKE_DIRECTORY ${post_configure_dir}) endif () - # if (NOT EXISTS ${post_configure_dir}/fud_version.h) - # file(COPY ${pre_configure_dir}/fud_version.h DESTINATION ${post_configure_dir}) + # if (NOT EXISTS ${post_configure_dir}/fud_config.h) + # file(COPY ${pre_configure_dir}/fud_config.h DESTINATION ${post_configure_dir}) # endif() if (NOT DEFINED GIT_HASH_CACHE) set(GIT_HASH_CACHE "INVALID") endif () - # Only update the fud_version.cpp if the hash has changed. This will + # Only update the fud_config.cpp if the hash has changed. This will # prevent us from rebuilding the project more than we need to. if (NOT ${GIT_HASH} STREQUAL ${GIT_HASH_CACHE} OR NOT EXISTS ${post_configure_file}) # Set che GIT_HASH_CACHE variable the next build won't have @@ -70,8 +70,8 @@ function(CheckGitSetup top_git_hash) BYPRODUCTS ${post_configure_file} ) - add_library(fud_version INTERFACE ${CMAKE_BINARY_DIR}/include/fud_version.hpp) - add_dependencies(fud_version AlwaysCheckGit) + add_library(fud_config INTERFACE ${CMAKE_BINARY_DIR}/include/fud_config.hpp) + add_dependencies(fud_config AlwaysCheckGit) CheckGitVersion(git_hash) set(${top_git_hash} ${git_hash} PARENT_SCOPE) diff --git a/cmake/fud_version.hpp.in b/cmake/fud_config.hpp.in index 5cce0e4..e83e9c3 100644 --- a/cmake/fud_version.hpp.in +++ b/cmake/fud_config.hpp.in @@ -8,7 +8,8 @@ namespace fud { constexpr uint8_t FudVersionMajor = @PROJECT_VERSION_MAJOR@; constexpr uint8_t FudVersionMinor = @PROJECT_VERSION_MINOR@; constexpr uint8_t FudVersionPatch = @PROJECT_VERSION_PATCH@; -constexpr const char GitHash[] = "@GIT_HASH@"; +constexpr const char FudGitHash[] = "@GIT_HASH@"; +static constexpr bool fudBoundsChecking = @FUD_BOUNDS_CHECKING@; } // namespace fud diff --git a/include/fud_algorithm.hpp b/include/fud_algorithm.hpp index e3d5d3b..0ad71d5 100644 --- a/include/fud_algorithm.hpp +++ b/include/fud_algorithm.hpp @@ -18,11 +18,11 @@ #ifndef FUD_ALGORITHM_HPP #define FUD_ALGORITHM_HPP +#include "fud_option.hpp" #include "fud_span.hpp" #include <concepts> #include <limits> -#include <optional> #include <type_traits> namespace fud { @@ -48,30 +48,41 @@ class Iota { { } - constexpr std::optional<T> operator()() noexcept + constexpr Iota(const Iota& rhs) noexcept = default; + + constexpr Iota(Iota&& rhs) noexcept = default; + + ~Iota() noexcept = default; + + Iota& operator=(const Iota& rhs) = default; + + Iota& operator=(Iota&& rhs) = default; + + constexpr Option<T> operator()() noexcept { auto value = m_value; if (m_increment > 0) { if (m_limit - m_increment < m_value) { - return std::nullopt; + return NullOpt; } } else { if (m_limit + m_increment + 1 >= m_value) { - return std::nullopt; + return NullOpt; } } m_value += m_increment; return value; } - void set(T value) { + void set(T value) + { m_value = value; } private: T m_value; - const T m_increment; - const T m_limit; + T m_increment; + T m_limit; }; template <typename T, size_t Size, typename Func> @@ -91,18 +102,19 @@ Span<T, Size> mapTo(Span<T, Size> input, Span<U, Size> output, Func&& mapFunc) output[idx] = std::forward<Func>(mapFunc)(input[idx]); } - return input; + return output; } -template <typename T, size_t Size, typename Func, typename Builder, typename Output> +template <typename T, size_t Size, typename Func, typename Builder> auto map(Span<T, Size> input, Func&& mapFunc, Builder&& builder) -> decltype(std::forward<Builder>(builder)()) { + using Output = decltype(std::forward<Builder>(builder)()); Output output{std::forward<Builder>(builder)()}; for (auto idx = 0; idx < input.size() && idx < output.size(); ++idx) { output[idx] = std::forward<Func>(mapFunc)(input[idx]); } - return input; + return output; } template <typename Generator, typename Builder> @@ -132,7 +144,7 @@ bool allOf(Generator&& generator, Func&& predicate) { bool result = true; while (auto val = std::forward<Generator>(generator)()) { - result = result && std::forward<Func>(predicate)(*val); + result = result && std::forward<Func>(predicate)(val.value()); } return result; } @@ -152,7 +164,7 @@ bool anyOf(Generator&& generator, Func&& predicate) { bool result = false; while (auto val = std::forward<Generator>(generator)()) { - result = result || std::forward<Func>(predicate)(*val); + result = result || std::forward<Func>(predicate)(val.value()); } return result; } diff --git a/include/fud_array.hpp b/include/fud_array.hpp index 807621a..dcbd54a 100644 --- a/include/fud_array.hpp +++ b/include/fud_array.hpp @@ -18,9 +18,10 @@ #ifndef FUD_ARRAY_HPP #define FUD_ARRAY_HPP -#include <cstddef> - #include "fud_memory.hpp" +#include "fud_span.hpp" + +#include <cstddef> namespace fud { @@ -106,6 +107,11 @@ struct Array { constexpr bool operator==(const Array<T, Size>&) const noexcept = default; constexpr auto operator<=>(const Array<T, Size>& other) const noexcept = default; + + Span<T, Size> span() + { + return Span<T, Size>{data(), Size}; + } }; } // namespace fud diff --git a/include/fud_memory.hpp b/include/fud_memory.hpp index 97328a9..6ce6312 100644 --- a/include/fud_memory.hpp +++ b/include/fud_memory.hpp @@ -57,6 +57,22 @@ constexpr void setMemory(Container<T, Size>& container, const T& value) } } +template <template <class, size_t> class Container, typename T, size_t Size> +constexpr void setMemory(Container<T, Size>& container, T&& value) +{ + for (auto& elt : container) { + elt = value; + } +} + +template <template <size_t> class Container, typename T, size_t Size> +constexpr void setMemory(Container<Size>& container, T&& value) +{ + for (auto& elt : container) { + elt = value; + } +} + template <size_t Count, typename T, typename U> void copyMem(T& destination, const U& source) { diff --git a/include/fud_option.hpp b/include/fud_option.hpp new file mode 100644 index 0000000..ca3954f --- /dev/null +++ b/include/fud_option.hpp @@ -0,0 +1,229 @@ +/* + * 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_OPTION_HPP +#define FUD_OPTION_HPP + +#include "fud_assert.hpp" + +#include <cstddef> +#include <functional> +#include <new> // IWYU pragma: keep (placement new) +#include <type_traits> + +namespace fud { + +namespace option_detail { + +struct NullOptionType { + enum class NullOptConstructor : char + { + Monostate + }; + + constexpr explicit NullOptionType(NullOptConstructor /*unnamed*/) + { + } +}; + +template <size_t Size> +struct DataArray { + // NOLINTBEGIN(cppcoreguidelines-avoid-c-arrays) + std::byte m_data[Size]; + // NOLINTEND(cppcoreguidelines-avoid-c-arrays) + + constexpr std::byte* data() noexcept + { + return m_data; + } + + [[nodiscard]] constexpr const std::byte* data() const noexcept + { + return m_data; + } + + constexpr bool operator==(const DataArray&) const noexcept = default; + + constexpr void clear() + { + for (size_t idx = 0; idx < Size; ++idx) { + m_data[idx] = std::byte(0); + } + } +}; + +} // namespace option_detail + +inline constexpr option_detail::NullOptionType NullOpt{option_detail::NullOptionType::NullOptConstructor::Monostate}; + +template <typename T> +class Option { + private: + static_assert(!std::is_same_v<T, option_detail::NullOptionType>); + static constexpr bool IsRef = std::is_reference_v<T>; + using ValueType = typename std::remove_reference<T>::type; + static constexpr size_t Size = IsRef ? sizeof(std::reference_wrapper<ValueType>) : sizeof(ValueType); + + public: + constexpr Option() noexcept : m_engaged{false} + { + } + + constexpr Option(option_detail::NullOptionType nullOpt) noexcept : m_engaged{false} + { + static_cast<void>(nullOpt); + } + + constexpr Option(T value) noexcept : m_engaged{true} + { + if constexpr (IsRef) { + new (m_data.data()) std::reference_wrapper<ValueType>(std::ref(value)); + if (!m_engaged) { + std::abort(); + } + } else { + new (m_data.data()) ValueType(value); + if (!m_engaged) { + std::abort(); + } + } + } + + constexpr Option(const Option& rhs) noexcept : m_engaged(rhs.m_engaged), m_data(rhs.m_data) + { + } + + constexpr Option(Option&& rhs) noexcept : m_engaged(rhs.m_engaged), m_data(std::move(rhs.m_data)) + { + rhs.cleanup(); + } + + ~Option() noexcept + { + destroy(); + } + + Option& operator=(const Option& rhs) noexcept + { + if (&rhs == this) { + return *this; + } + destroy(); + m_engaged = rhs.m_engaged; + m_data = rhs.m_data; + return *this; + } + + Option& operator=(Option&& rhs) noexcept + { + destroy(); + m_engaged = rhs.m_engaged; + m_data = std::move(rhs.m_data); + rhs.cleanup(); + return *this; + } + + [[nodiscard]] bool hasValue() const + { + return m_engaged; + } + + operator bool() const { + return hasValue(); + } + + [[nodiscard]] constexpr const ValueType& value() const& + { + fudAssert(m_engaged); + if constexpr (IsRef) { + return *reinterpret_cast<const std::reference_wrapper<ValueType>*>(m_data.data()); + } else { + return *reinterpret_cast<const ValueType*>(m_data.data()); + } + } + + [[nodiscard]] constexpr ValueType& value() & + { + fudAssert(m_engaged); + if constexpr (IsRef) { + return *reinterpret_cast<std::reference_wrapper<ValueType>*>(m_data.data()); + } else { + return *reinterpret_cast<ValueType*>(m_data.data()); + } + } + + [[nodiscard]] constexpr const ValueType&& value() const&& + { + fudAssert(m_engaged); + static_assert(!IsRef); + return *reinterpret_cast<const ValueType*>(m_data.data()); + } + + template <typename F> + constexpr auto map(F&& func) const & -> Option<decltype(std::forward<F>(func)(value()))> + { + using U = decltype(std::forward<F>(func)(value())); + // static_assert(std::is_same_v<decltype(std::forward<F>(func)(value())), Option<U>>()); + if (hasValue()) { + return Option<U>{std::forward<F>(func)(value())}; + } + return Option<U>{NullOpt}; + } + + private: + constexpr void destroy() noexcept + { + if (m_engaged) { + if constexpr (IsRef) { + // reinterpret_cast<std::reference_wrapper<ValueType>*>(m_data.data()); + } else { + reinterpret_cast<ValueType*>(m_data.data())->~ValueType(); + } + cleanup(); + } + } + + constexpr void cleanup() noexcept + { + m_engaged = false; + m_data.clear(); + } + + // alignas(maxAlign) Array<uint8_t, maxSize> priv_m_data; + + alignas(alignof(T)) option_detail::DataArray<Size> m_data{}; + + bool m_engaged; +}; + +namespace test { + +void testOption() +{ + Option<int> intOpt; + static_cast<void>(intOpt); + Option<int&> intRefNull; + static_cast<void>(intRefNull); + int value; + Option<int&> intRefValue{value}; +} + +} // namespace test + +} // namespace fud + +#endif diff --git a/include/fud_span.hpp b/include/fud_span.hpp index 5b8497e..ed4bcc7 100644 --- a/include/fud_span.hpp +++ b/include/fud_span.hpp @@ -18,7 +18,6 @@ #ifndef FUD_SPAN_HPP #define FUD_SPAN_HPP -#include "fud_array.hpp" #include "fud_result.hpp" #include "fud_status.hpp" @@ -27,11 +26,17 @@ namespace fud { +template <typename T, size_t Size> +struct Array; + template <typename T, size_t Size = SIZE_MAX> struct Span { static_assert(Size > 0); using ValueType = T; + T* m_data; + const size_t m_size; + static Span make(Array<T, Size>& array) { Span<T, Size> output{array.data(), Size}; @@ -89,9 +94,6 @@ struct Span { return output; } - T* m_data; - const size_t m_size; - [[nodiscard]] constexpr size_t size() const { if constexpr (Size < SIZE_MAX) { diff --git a/include/fud_status.hpp b/include/fud_status.hpp index 91048ac..d57a9c5 100644 --- a/include/fud_status.hpp +++ b/include/fud_status.hpp @@ -23,24 +23,26 @@ namespace fud { enum class [[nodiscard]] FudStatus { Success = 0, + Partial, + Failure, NullPointer, - StringInvalid, - ObjectInvalid, - OperationInvalid, - AllocFailure, - DeallocFailure, ArgumentInvalid, + VariantInvalid, + ObjectInvalid, Utf8Invalid, - Failure, + StringInvalid, + OperationInvalid, + AlreadyInitialized, + FormatInvalid, + RangeError, + IndexInvalid, + Exists, NotFound, - Aliased, Empty, - Partial, Full, - RangeError, - VariantInvalid, - BadArrayLength, - FormatInvalid, + Aliased, + AllocFailure, + DeallocFailure, NotImplemented, NotSupported }; @@ -50,6 +52,10 @@ constexpr const char* FudStatusToString(FudStatus status) switch (status) { case FudStatus::Success: return "Success"; + case FudStatus::Partial: + return "Partial"; + case FudStatus::Failure: + return "Failure"; case FudStatus::NullPointer: return "NullPointer"; case FudStatus::StringInvalid: @@ -58,34 +64,34 @@ constexpr const char* FudStatusToString(FudStatus status) return "ObjectInvalid"; case FudStatus::OperationInvalid: return "OperationInvalid"; - case FudStatus::AllocFailure: - return "AllocFailure"; - case FudStatus::DeallocFailure: - return "DeallocFailure"; case FudStatus::ArgumentInvalid: return "ArgumentInvalid"; case FudStatus::Utf8Invalid: return "Utf8Invalid"; - case FudStatus::Failure: - return "Failure"; - case FudStatus::NotFound: - return "NotFound"; - case FudStatus::Aliased: - return "Aliased"; - case FudStatus::Empty: - return "Empty"; - case FudStatus::Partial: - return "Partial"; - case FudStatus::Full: - return "Full"; case FudStatus::RangeError: return "RangeError"; case FudStatus::VariantInvalid: return "VariantInvalid"; - case FudStatus::BadArrayLength: - return "BadArrayLength"; case FudStatus::FormatInvalid: return "FormatInvalid"; + case FudStatus::AlreadyInitialized: + return "AlreadyInitialized"; + case FudStatus::IndexInvalid: + return "IndexInvalid"; + case FudStatus::Exists: + return "Exists"; + case FudStatus::NotFound: + return "NotFound"; + case FudStatus::Empty: + return "Empty"; + case FudStatus::Full: + return "Full"; + case FudStatus::Aliased: + return "Aliased"; + case FudStatus::AllocFailure: + return "AllocFailure"; + case FudStatus::DeallocFailure: + return "DeallocFailure"; case FudStatus::NotImplemented: return "NotImplemented"; case FudStatus::NotSupported: diff --git a/include/fud_vector.hpp b/include/fud_vector.hpp index 56e1659..f90819a 100644 --- a/include/fud_vector.hpp +++ b/include/fud_vector.hpp @@ -19,42 +19,654 @@ #define FUD_VECTOR_HPP #include "fud_allocator.hpp" +#include "fud_assert.hpp" +#include "fud_config.hpp" +#include "fud_option.hpp" #include "fud_result.hpp" +#include "fud_span.hpp" #include "fud_status.hpp" #include <cstddef> +#include <functional> +#include <new> // IWYU pragma: keep (placement new) namespace fud { template <typename T> class Vector { + static constexpr size_t ElementSize = sizeof(T); + static constexpr size_t Alignment = alignof(T); + public: - static Result<Vector<T>, FudStatus> from(const Vector<T>& rhs); + constexpr Vector() noexcept = default; + constexpr Vector(const Vector<T>& rhs) = delete; + constexpr Vector(Vector<T>&& rhs) noexcept : + m_allocator(rhs.m_allocator), m_data(rhs.m_data), m_length{rhs.m_length}, m_capacity{rhs.m_capacity} + { + rhs.m_allocator = nullptr; + rhs.m_data = nullptr; + rhs.m_length = 0; + rhs.m_capacity = 0; + } + + ~Vector() noexcept + { + static_cast<void>(cleanup()); + } + + Vector& operator=(const Vector<T>& rhs) = delete; + + Vector& operator=(Vector<T>&& rhs) noexcept + { + cleanup(); + m_allocator = rhs.m_allocator; + m_data = rhs.m_data; + m_length = rhs.m_length; + m_capacity = rhs.m_length; + + rhs.m_allocataor = nullptr; + rhs.m_data = nullptr; + rhs.m_length = 0; + rhs.m_capacity = 0; + } + + static Result<Vector<T>, FudStatus> withCapacity(size_t capacity, Allocator* allocator = &globalFudAllocator) + { + Vector<T> output{}; + auto status = initializeWithCapacity(output, capacity, allocator); + if (status != FudStatus::Success) { + return status; + } + return output; + } + + static FudStatus initializeWithCapacity( + Vector<T>& output, + size_t capacity, + Allocator* allocator = &globalFudAllocator) + { + if (output.m_data != nullptr) { + return FudStatus::AlreadyInitialized; + } + + if (allocator == nullptr) { + return FudStatus::NullPointer; + } + + if (capacity > SIZE_MAX / ElementSize) { + return FudStatus::ArgumentInvalid; + } + + size_t requestedSize = capacity * ElementSize; + auto dataPtrResult = allocator->allocate(requestedSize, Alignment); + if (dataPtrResult.isError()) { + return dataPtrResult.getError(); + } + + output.m_allocator = allocator; + output.m_data = static_cast<T*>(dataPtrResult.getOkay()); + output.m_length = 0; + output.m_capacity = capacity; + return FudStatus::Success; + } + + static Result<Vector<T>, FudStatus> withSize(size_t count, Allocator* allocator = &globalFudAllocator) + { + Vector<T> output{}; + auto status = initializeWithCapacity(output, count, allocator); + if (status != FudStatus::Success) { + return status; + } + return output; + } + + static Result<Vector<T>, FudStatus> initializeWithSize( + Vector<T>& output, + size_t count, + Allocator* allocator = &globalFudAllocator) + { + if (output.m_data != nullptr) { + return FudStatus::AlreadyInitialized; + } + + auto status = Vector::initializeWithCapacity(output, count, allocator); + if (status != FudStatus::Success) { + return status; + } + + output.m_length = count; + for (size_t index = 0; index < count; ++index) { + const auto* ptr = new (output.m_data + index) T(); + fudAssert(ptr != nullptr); + } + return output; + } + + template <typename Builder> + static Result<Vector<T>, FudStatus> withSizeFallible( + size_t count, + Builder&& builder, + Allocator* allocator = &globalFudAllocator) + { + Vector<T> output{}; + auto status = initializeWithSizeFallible(output, count, std::forward<Builder>(builder), allocator); + if (status != FudStatus::Success) { + return status; + } + return output; + } + + template <typename Builder> + static Result<Vector<T>, FudStatus> initializeWithSizeFallible( + Vector<T>& output, + size_t count, + Builder&& builder, + Allocator* allocator = &globalFudAllocator) + { + using BuilderResult = decltype(std::forward<Builder>(builder)()); + static_assert(std::is_same_v<BuilderResult, FudStatus>()); + + auto status = Vector::initializeWithCapacity(output, count, allocator); + if (status != FudStatus::Success) { + return status; + } + + output.m_length = count; + + for (size_t index = 0; index < count; ++index) { + auto builderResult{std::forward<Builder>(builder)(output.m_data[index])}; + if (builderResult.isError()) { + return builderResult.takeError(); + } + } + + return output; + } - static Vector<T> move(Vector<T>&& rhs); + static Result<Vector<T>, FudStatus> from(const Vector<T>& rhs, Option<Allocator*> allocatorOption = NullOpt) + { + Allocator* allocator = nullptr; + if (allocatorOption.hasValue()) { + allocator = allocatorOption.value(); + if (allocator == nullptr) { + return FudStatus::NullPointer; + } + } else { + allocator = rhs.allocator; + if (allocator == nullptr) { + return FudStatus::ArgumentInvalid; + } + } + + fudAssert(rhs.m_length <= rhs.m_capacity); + + auto spanResult = rhs.span(); + if (spanResult.isError()) { + return spanResult.takeError(); + } + Vector<T> output{}; + auto status = Vector::initializeFromSpan(output, rhs.m_length, allocator); + if (status != FudStatus::Success) { + return status; + } + return output; + } + + template <size_t Size> + static Result<Vector<T>, FudStatus> from(Span<const T, Size>& rhs, Allocator* allocator) + { + Vector<T> output{}; + auto status = initializeFromSpan(output, rhs, allocator); + if (status != FudStatus::Success) { + return status; + } + return output; + } + + template <size_t Size> + static FudStatus initializeFromSpan(Vector<T>& output, Span<const T, Size>& rhs, Allocator* allocator) + { + auto status = Vector::initializeWithCapacity(output, rhs.size(), allocator); + if (status != FudStatus::Success) { + return status; + } + output.m_length = rhs.m_length; + for (size_t index = 0; index < output.m_length; ++index) { + output.m_data[index] = rhs[index]; + } + } + + static Vector<T> move(Vector<T>&& rhs) noexcept + { + return Vector<T>{std::move(rhs)}; + } FudStatus copy(const Vector<T>& rhs); FudStatus take(Vector<T>&& rhs); - [[nodiscard]] size_t size() const { + [[nodiscard]] size_t size() const + { return m_length; } - [[nodiscard]] size_t capacity() const { + [[nodiscard]] size_t capacity() const + { return m_capacity; } - FudStatus reserve(); + Result<Span<const T>, FudStatus> span() const + { + if (m_data == nullptr) { + return FudStatus::ObjectInvalid; + } + return Span{m_data, m_length}; + } + + Result<Span<T>, FudStatus> span() + { + if (m_data == nullptr) { + return FudStatus::ObjectInvalid; + } + return Span{m_data, m_length}; + } + + Result<Span<const T>, FudStatus> span(size_t count) const + { + if (m_data == nullptr) { + return FudStatus::ObjectInvalid; + } + if (count > m_length) { + return FudStatus::ArgumentInvalid; + } + return Span{m_data, count}; + } + + Result<Span<T>, FudStatus> span(size_t count) + { + if (m_data == nullptr) { + return FudStatus::ObjectInvalid; + } + if (count > m_length) { + return FudStatus::ArgumentInvalid; + } + return Span{m_data, count}; + } + + Result<Span<const T>, FudStatus> span(size_t start, size_t count) const + { + if (m_data == nullptr) { + return FudStatus::ObjectInvalid; + } + if (SIZE_MAX - start < m_length || start + count > m_length) { + return FudStatus::ArgumentInvalid; + } + return Span{m_data + start, count}; + } + + Result<Span<T>, FudStatus> span(size_t start, size_t count) + { + if (m_data == nullptr) { + return FudStatus::ObjectInvalid; + } + if (SIZE_MAX - start < m_length || start + count > m_length) { + return FudStatus::ArgumentInvalid; + } + return Span{m_data + start, count}; + } + + FudStatus reserve(size_t count) + { + if (count <= m_capacity) { + return FudStatus::Success; + } + + if (m_allocator == nullptr) { + return FudStatus::ObjectInvalid; + } + + if (count > SIZE_MAX / ElementSize) { + return FudStatus::ArgumentInvalid; + } + + size_t requestedSize = count * ElementSize; + auto dataPtrResult = m_allocator->allocate(requestedSize, Alignment); + if (dataPtrResult.isError()) { + return dataPtrResult.takeError(); + } + + auto* dataPtr = static_cast<T*>(dataPtrResult.takeOkay()); + for (size_t index = 0; index < m_length; ++index) { + const auto* ptr = new (dataPtr + index) T(std::move(m_data[index])); + fudAssert(ptr != nullptr); + m_data[index].~T(); + } + + m_data = dataPtr; + m_capacity = count; + + return FudStatus::Success; + } + + FudStatus resize(size_t count) + { + if (count == m_length) { + return FudStatus::Success; + } + + if (m_allocator == nullptr) { + return FudStatus::ObjectInvalid; + } - FudStatus resize(); + if (count < m_length) { + for (size_t index = count; index < m_length; ++index) { + m_data[index].~T(); + } + m_length = count; + return FudStatus::Success; + } - FudStatus clear(); + auto reserveStatus = reserve(count); + if (reserveStatus != FudStatus::Success) { + return reserveStatus; + } - // FudResult at(); + for (size_t index = m_length; index < count; ++index) { + const auto* ptr = new (m_data + index) T(); + fudAssert(ptr != nullptr); + } + + m_length = count; + return FudStatus::Success; + } + + FudStatus clear() + { + if (m_allocator == nullptr || m_data == nullptr) { + return FudStatus::ObjectInvalid; + } + for (size_t index = 0; index < m_length; ++index) { + m_data[index].~T(); + } + m_length = 0; + return FudStatus::Success; + } + + Result<std::reference_wrapper<T>, FudStatus> get(size_t index) + { + if (m_data == nullptr) { + return FudStatus::ObjectInvalid; + } + if (index >= m_length) { + return FudStatus::IndexInvalid; + } + return std::ref(m_data[index]); + } + + Result<const std::reference_wrapper<const T>, FudStatus> ref(size_t index) const + { + if (m_data == nullptr) { + return FudStatus::ObjectInvalid; + } + if (index >= m_length) { + return FudStatus::IndexInvalid; + } + return std::cref(m_data[index]); + } + + constexpr Option<T&> front() + { + if (m_length > 0) { + fudAssert(m_data != nullptr); + return m_data[0]; + } + return NullOpt; + } + + constexpr Option<const T&> front() const + { + if (m_length > 0) { + fudAssert(m_data != nullptr); + return m_data[0]; + } + return NullOpt; + } + + constexpr Option<T&> back() + { + if (m_length > 0) { + fudAssert(m_data != nullptr); + return m_data[m_length - 1]; + } + return NullOpt; + } + + constexpr Option<const T&> back() const + { + if (m_length > 0) { + return m_data[m_length - 1]; + } + return NullOpt; + } + + constexpr T* data() noexcept + { + return m_data; + } + + constexpr const T* data() const noexcept + { + return m_data; + } + + constexpr T* begin() noexcept + { + return m_data; + } + + constexpr const T* begin() const noexcept + { + return m_data; + } + + constexpr T* end() noexcept + { + return m_data + m_length; + } + + constexpr const T* end() const noexcept + { + return m_data + m_length; + } + + constexpr T& operator[](size_t index) + { + if constexpr (fudBoundsChecking) { + fudAssert(m_data != nullptr); + fudAssert(index < m_length); + } + return m_data[index]; + } + + constexpr const T& operator[](size_t index) const + { + if constexpr (fudBoundsChecking) { + fudAssert(m_data != nullptr); + fudAssert(index < m_length); + } + return m_data[index]; + } + + FudStatus pushBack(const T& value) + { + if (m_length == m_capacity) { + auto status = grow(); + if (status != FudStatus::Success) { + return status; + } + } + const auto* ptr = new (m_data + m_length) T(value); + fudAssert(ptr != nullptr); + m_length++; + return FudStatus::Success; + } + + FudStatus pushBack(T&& value) + { + if (m_length == m_capacity) { + auto status = grow(); + if (status != FudStatus::Success) { + return status; + } + } + const auto* ptr = new (m_data + m_length) T(std::move(value)); + fudAssert(ptr != nullptr); + m_length++; + return FudStatus::Success; + } + + Result<T, FudStatus> popBack() + { + if (m_data == nullptr) { + return FudStatus::ObjectInvalid; + } + if (m_length == 0) { + return FudStatus::Empty; + } + auto result{std::move(m_data[m_length - 1])}; + m_length--; + m_data[m_length].~T(); + return result; + } + + FudStatus eraseBack() + { + if (m_data == nullptr) { + return FudStatus::ObjectInvalid; + } + if (m_length == 0) { + return FudStatus::Empty; + } + m_length--; + m_data[m_length].~T(); + return FudStatus::Success; + } + + FudStatus insert(size_t index, const T& value) + { + if (index > m_length) { + return FudStatus::IndexInvalid; + } + if (index == m_length) { + return pushBack(value); + } + if (m_length == m_capacity) { + auto status = grow(); + if (status != FudStatus::Success) { + return status; + } + } + + const auto* ptr = new (m_data + m_length) T(std::move(m_data[m_length - 1])); + fudAssert(ptr != nullptr); + + for (size_t backIndex = m_length - 1; backIndex > index; --backIndex) { + m_data[backIndex] = std::move(m_data[backIndex - 1]); + } + m_data[index] = value; + m_length++; + return FudStatus::Success; + } + + FudStatus insert(size_t index, T&& value) + { + if (index > m_length) { + return FudStatus::IndexInvalid; + } + if (index == m_length) { + return pushBack(std::move(value)); + } + if (m_length == m_capacity) { + auto status = grow(); + if (status != FudStatus::Success) { + return status; + } + } + + const auto* ptr = new (m_data + m_length) T(std::move(m_data[m_length - 1])); + fudAssert(ptr != nullptr); + + for (size_t backIndex = m_length - 1; backIndex > index; --backIndex) { + m_data[backIndex] = std::move(m_data[backIndex - 1]); + } + m_data[index] = std::move(value); + m_length++; + return FudStatus::Success; + } + + FudStatus erase(size_t index) + { + if (index >= m_length) { + return FudStatus::IndexInvalid; + } + + m_data[index].~T(); + for (size_t fwdIndex = index; fwdIndex + 1 < m_length; fwdIndex++) + { + m_data[fwdIndex] = std::move(m_data[fwdIndex + 1]); + } + m_data[m_length - 1].~T(); + m_length--; + return FudStatus::Success; + } private: + FudStatus grow() + { + // See https://github.com/facebook/folly/blob/main/folly/docs/FBVector.md + size_t additional = m_capacity < 2 ? 1 : m_capacity / 2; + if (SIZE_MAX - additional * ElementSize < m_capacity * ElementSize) { + additional = SIZE_MAX - m_capacity * ElementSize / 2; + } + while (additional > 0) { + auto reserveStatus = reserve(additional + m_capacity); + if (reserveStatus == FudStatus::Success) { + break; + } + if (reserveStatus == FudStatus::AllocFailure) { + additional /= 2; + } else { + return reserveStatus; + } + } + + if (m_length == m_capacity) { + return FudStatus::AllocFailure; + } + + return FudStatus::Success; + } + + FudStatus cleanup() noexcept + { + auto status = clear(); + + if (m_data != nullptr && m_allocator != nullptr) { + auto deallocStatus = m_allocator->deallocate(m_data, m_capacity); + if (status == FudStatus::Success) { + status = deallocStatus; + } + } + + m_allocator = nullptr; + m_data = nullptr; + m_length = 0; + m_capacity = 0; + return status; + } + Allocator* m_allocator{&globalFudAllocator}; + T* m_data{nullptr}; size_t m_length{0}; size_t m_capacity{0}; }; diff --git a/source/fud_string_view.cpp b/source/fud_string_view.cpp index fdb63b3..1cc73a6 100644 --- a/source/fud_string_view.cpp +++ b/source/fud_string_view.cpp @@ -1066,7 +1066,7 @@ FudStatus stringViewToFloat(StringView input, T& number, size_t& index) if (std::isinf(num) || std::isnan(num)) // isnan is dubious here - likely unreachable { - return ExtRangeError; + return FudStatus::RangeError; } return retSuccess(); diff --git a/source/libfud.cpp b/source/libfud.cpp index 802b2c7..be43490 100644 --- a/source/libfud.cpp +++ b/source/libfud.cpp @@ -17,8 +17,7 @@ #include "libfud.hpp" -#include "fud_assert.hpp" -#include "fud_version.hpp" +#include "fud_config.hpp" #include <cstdlib> @@ -27,12 +26,11 @@ namespace fud { FUD fud() { FUD fudInfo{}; - static_assert(sizeof(GitHash) >= sizeof(fudInfo.revision)); + static_assert(sizeof(FudGitHash) >= sizeof(fudInfo.revision)); fudInfo.major = FudVersionMajor; fudInfo.minor = FudVersionMinor; fudInfo.patch = FudVersionPatch; - auto copyResult = copyMem(fudInfo.revision.data(), fudInfo.revision.size(), GitHash, fudInfo.revision.size() - 1); - fudAssert(copyResult == FudStatus::Success); + copyMem<sizeof(fudInfo.revision) - 1>(fudInfo.revision, FudGitHash); fudInfo.revision[fudInfo.revision.size() - 1] = '\0'; return fudInfo; } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index c4d957b..515ae16 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -62,11 +62,13 @@ fud_add_test(test_assert SOURCES test_assert.cpp) # fud_add_test(test_c_file SOURCES test_c_file.cpp) fud_add_test(test_directory SOURCES test_directory.cpp) fud_add_test(test_format SOURCES test_format.cpp) +fud_add_test(test_option SOURCES test_option.cpp) fud_add_test(test_result SOURCES test_result.cpp) fud_add_test(test_span SOURCES test_span.cpp) fud_add_test(test_sqlite SOURCES test_sqlite.cpp) fud_add_test(test_string SOURCES test_string.cpp) fud_add_test(test_utf8 SOURCES test_utf8.cpp) +fud_add_test(test_vector SOURCES test_vector.cpp) # fud_add_test(test_deserialize_number SOURCES test_deserialize_number.cpp) # fud_add_test(test_ext_algorithm SOURCES test_algorithm.cpp) diff --git a/test/test_common.hpp b/test/test_common.hpp index 0ca8eb4..f049fed 100644 --- a/test/test_common.hpp +++ b/test/test_common.hpp @@ -38,10 +38,16 @@ static_assert(sizeof(FOUR_BYTE) == 4 + 1); #define LOWERCASE_CHARS "abcdefghijklmnopqrstuvwxyz" #define UPPERCASE_CHARS "ABCDEFGHIJKLMNOPQRSTUVWXYZ" #define DECIMAL_CHARS "0123456789" +#define HEX_LOWER "abcdef" +#define HEX_UPPER "ABCDEF" +#define HEX_CHARS HEX_LOWER HEX_UPPER DECIMAL_CHARS #define ALPHA_CHARS LOWERCASE_CHARS UPPERCASE_CHARS #define ALPHA_NUMERIC_CHARS ALPHA_CHARS DECIMAL_CHARS #define PUNCTUATION_CHARS "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~" #define GRAPHICAL_CHARS ALPHA_NUMERIC_CHARS PUNCTUATION_CHARS +#define BLANK_CHARS " \t" +#define SPACE_CHARS BLANK_CHARS "\v\r\n" +#define PRINTABLE_CHARS " " LOWERCASE_CHARS UPPERCASE_CHARS DECIMAL_CHARS PUNCTUATION_CHARS #define CHARACTER_SET LOWERCASE_CHARS " " UPPERCASE_CHARS // NOLINTEND(cppcoreguidelines-macro-usage) diff --git a/test/test_fud.cpp b/test/test_fud.cpp index 0778f98..72d569a 100644 --- a/test/test_fud.cpp +++ b/test/test_fud.cpp @@ -15,10 +15,11 @@ * limitations under the License. */ -#include "fud_version.hpp" +#include "fud_config.hpp" #include "libfud.hpp" #include "gtest/gtest.h" +#include <cstdlib> namespace fud { @@ -32,11 +33,33 @@ TEST(FudTest, FudFud) auto compareResult = compareMem( fudInfo.revision.data(), fudInfo.revision.size(), - GitHash, + FudGitHash, fudInfo.revision.size() - 1); ASSERT_TRUE(compareResult.isOkay()); EXPECT_EQ(compareResult.getOkay(), 0); EXPECT_EQ(fudInfo.revision[fudInfo.revision.size() - 1], '\0'); } +TEST(FudTest, GetEnv) +{ + constexpr const char* testVarName = "FUD_TEST_VAR"; + constexpr const char* testVarValue = "FUD_TEST_VALUE"; + + ASSERT_EQ(unsetenv(testVarName), 0); + + auto fudVarResult = getEnv(nullptr); + ASSERT_TRUE(fudVarResult.isError()); + ASSERT_EQ(fudVarResult.getError(), FudStatus::NullPointer); + + fudVarResult = getEnv(testVarName); + ASSERT_TRUE(fudVarResult.isError()); + ASSERT_EQ(fudVarResult.getError(), FudStatus::NotFound); + + ASSERT_EQ(setenv(testVarName, testVarValue, 1), 0); + fudVarResult = getEnv(testVarName); + ASSERT_TRUE(fudVarResult.isOkay()); + auto fudVar{fudVarResult.takeOkay()}; + ASSERT_STREQ(fudVar.c_str(), testVarValue); +} + } // namespace fud diff --git a/test/test_option.cpp b/test/test_option.cpp new file mode 100644 index 0000000..a503a5f --- /dev/null +++ b/test/test_option.cpp @@ -0,0 +1,54 @@ +/* + * 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_option.hpp" + +#include "gtest/gtest.h" + +namespace fud { + +TEST(OptionTest, OptionCreation) +{ + static_assert(sizeof(std::reference_wrapper<int>) == 8); + int value = 42; + Option<int> maybeValue{}; + ASSERT_FALSE(maybeValue.hasValue()); + maybeValue = value; + ASSERT_TRUE(maybeValue.hasValue()); +} + +TEST(OptionTest, OptionRef) +{ + int value = 42; + Option<int&> optionalValueRef{value}; + ASSERT_TRUE(optionalValueRef.hasValue()); + ASSERT_EQ(optionalValueRef.value(), 42); + optionalValueRef.value() = 7; + ASSERT_EQ(value, 7); + + value = 42; + int otherValue = 13; + optionalValueRef = otherValue; + ASSERT_TRUE(optionalValueRef.hasValue()); + ASSERT_EQ(optionalValueRef.value(), otherValue); + optionalValueRef.value() = 7; + ASSERT_EQ(optionalValueRef.value(), otherValue); + ASSERT_EQ(otherValue, 7); + ASSERT_EQ(value, 42); +} + +} // namespace fud diff --git a/test/test_utf8.cpp b/test/test_utf8.cpp index 8f1d655..d1737f7 100644 --- a/test/test_utf8.cpp +++ b/test/test_utf8.cpp @@ -38,10 +38,21 @@ constexpr char printableCharOffset = 0x20; constexpr auto invalidAscii = FudUtf8::invalidAsciiCode.character(); -auto generateInvalidAsciiChars() +auto invalidAsciiGenerator() { - Iota<utf8> iota{}; - return generate([]() { return Array<utf8, invalidAsciiSize>{}; }, [&]() { return iota().value(); }); + return Iota<uint16_t>{validAsciiSize, 1, invalidAsciiSize}; +} + +template <typename T> +auto toUtf8(T letter) +{ + return FudUtf8::make(static_cast<utf8>(letter)); +} + +template <typename T> +auto toLetter(T letter) +{ + return static_cast<char>(letter); } TEST(Utf8Test, Utf8Creation) @@ -187,123 +198,80 @@ TEST(Utf8Test, Utf8IsAscii) Iota<int16_t> charIota{0, 1, validAsciiSize}; - ASSERT_TRUE(allOf( - [&]() -> std::optional<char> { - auto value = charIota(); - return value ? std::optional<char>(static_cast<char>(*value)) : std::nullopt; - }, - charIsAscii)); + ASSERT_TRUE(allOf([&]() -> Option<char> { return charIota().map(toLetter<int16_t>); }, charIsAscii)); Iota<int16_t> invalidCharIota{validAsciiSize, 1, invalidAsciiSize}; - ASSERT_FALSE(anyOf( - [&]() -> std::optional<char> { - auto value = invalidCharIota(); - return value ? std::optional<char>(static_cast<char>(*value)) : std::nullopt; - }, - charIsAscii)); + ASSERT_FALSE(anyOf([&]() -> Option<char> { return invalidCharIota().map(toLetter<int16_t>); }, charIsAscii)); FudUtf8 unicode{FudUtf8::invalidAscii()}; ASSERT_FALSE(utf8IsAscii(unicode)); charIota.set(0); - ASSERT_TRUE(allOf( - [&]() -> std::optional<FudUtf8> { - auto value = charIota(); - return value ? std::optional<FudUtf8>(FudUtf8::make(static_cast<utf8>(*value))) : std::nullopt; - }, - utf8IsAscii)); + ASSERT_TRUE(allOf([&]() -> Option<FudUtf8> { return charIota().map(toUtf8<int16_t>); }, utf8IsAscii)); invalidCharIota.set(invalidAsciiSize); - ASSERT_FALSE(anyOf( - [&]() -> std::optional<FudUtf8> { - auto value = invalidCharIota(); - return value ? std::optional<FudUtf8>(FudUtf8::make(static_cast<utf8>(*value))) : std::nullopt; - }, - utf8IsAscii)); + ASSERT_FALSE(anyOf([&]() -> Option<FudUtf8> { return invalidCharIota().map(toUtf8<int16_t>); }, utf8IsAscii)); } -TEST(Utf8Test, Utf8IsAlphaNumeric) +template <typename T, size_t Size = SIZE_MAX> +struct SpanGenerator { + Span<T, Size> span; + size_t index{0}; + + void reset() + { + index = 0; + } + + Option<T> operator()() + { + if (index < span.size()) { + index++; + return span[index - 1]; + } + return NullOpt; + } +}; + +TEST(Utf8Test, Utf8IsAlphanumeric) { - constexpr size_t numAlphaNumericChars = 26 * 2 + 10; - Array<char, numAlphaNumericChars + 1> alphaNumericCharLiteral{ALPHA_NUMERIC_CHARS}; - Array<char, numAlphaNumericChars> alphaNumericChars{}; - copyMem<numAlphaNumericChars>(alphaNumericChars, alphaNumericCharLiteral); -#if 0 - ASSERT_TRUE(allOf(alphaNumericChars, charIsAlphanumeric)); - - auto alphaNumericSetResult{StaticSet<char, numAlphaNumericChars>::makeFromArray(alphaNumericChars)}; - ASSERT_TRUE(alphaNumericSetResult.isOkay()); - auto alphaNumericSet{std::move(alphaNumericSetResult.getOkay())}; - - constexpr size_t numNonAlphaNumericChars = validAsciiSize - numAlphaNumericChars; - FixedVector<char, numNonAlphaNumericChars> nonAlphaNumericChars{}; + constexpr size_t numAlphanumericChars = 26 * 2 + 10; + Array<char, numAlphanumericChars + 1> alphanumericCharLiteral{ALPHA_NUMERIC_CHARS}; + Array<char, numAlphanumericChars> alphanumericChars{}; + copyMem<numAlphanumericChars>(alphanumericChars, alphanumericCharLiteral); + + ASSERT_TRUE(allOf(alphanumericChars.span(), charIsAlphanumeric)); + + constexpr size_t numNonAlphanumericChars = validAsciiSize - numAlphanumericChars; + Vector<char> nonAlphanumericChars{}; for (char idx = 0; idx < INT8_MAX; ++idx) { - if (!alphaNumericSet.isKey(idx)) { - ASSERT_TRUE(nonAlphaNumericChars.pushBack(idx)); + if (!charIsAlphanumeric(idx)) { + ASSERT_EQ(nonAlphanumericChars.pushBack(idx), FudStatus::Success); } } - ASSERT_FALSE(anyOf(nonAlphaNumericChars, charIsAlphanumeric)); + auto nonAlphanumericSpan{nonAlphanumericChars.span().takeOkay()}; + ASSERT_FALSE(anyOf(nonAlphanumericSpan, charIsAlphanumeric)); - auto invalidAsciiChars = generateInvalidAsciiChars(); + auto invalidAsciiChars = invalidAsciiGenerator(); ASSERT_FALSE(anyOf(invalidAsciiChars, charIsAlphanumeric)); - ASSERT_TRUE(allOf( - map(alphaNumericChars, [](auto input) { return ExtUtf8Char4{static_cast<utf8>(input)}; }), - ext_lib_char4_is_alphanumeric)); - ASSERT_FALSE(anyOf( - map(nonAlphaNumericChars, [](auto input) { return ExtUtf8Char4{static_cast<utf8>(input)}; }), - ext_lib_char4_is_alphanumeric)); - ASSERT_FALSE(anyOf( - map(invalidAsciiChars, [](auto input) { return ExtUtf8Char4{static_cast<utf8>(input)}; }), - ext_lib_char4_is_alphanumeric)); - - ASSERT_TRUE(allOf( - map(alphaNumericChars, [](auto input) { return ExtUtf8Char4{static_cast<utf8>(input)}; }), - ext_lib_char4_is_alphanumeric)); - ASSERT_FALSE(anyOf( - map(nonAlphaNumericChars, [](auto input) { return ExtUtf8Char4{static_cast<utf8>(input)}; }), - ext_lib_char4_is_alphanumeric)); - ASSERT_FALSE(anyOf( - map(invalidAsciiChars, [](auto input) { return ExtUtf8Char4{static_cast<utf8>(input)}; }), - ext_lib_char4_is_alphanumeric)); - - Utf8 utf8{invalidAscii}; - bool isAscii = false; - ASSERT_EQ(ext_lib_utf8_is_alphanumeric(nullptr, nullptr), ExtNullPointer); - ASSERT_EQ(ext_lib_utf8_is_alphanumeric(&utf8, nullptr), ExtNullPointer); - ASSERT_EQ(ext_lib_utf8_is_alphanumeric(nullptr, &isAscii), ExtNullPointer); - ASSERT_EQ(ext_lib_utf8_is_alphanumeric(&utf8, &isAscii), ExtInvalidInput); - ASSERT_FALSE(isAscii); - - ASSERT_TRUE(allOf( - map(alphaNumericChars, - [](char letter) { return FudUtf8::makeUtf8(ExtUtf8Char4{static_cast<utf8>(letter)}); }), - [](auto& utf8Letter) { - bool predicate = false; - auto isPredicateStatus = ext_lib_utf8_is_alphanumeric(&utf8Letter, &predicate); - return isPredicateStatus == ExtSuccess && predicate; - })); - ASSERT_FALSE(anyOf( - map(nonAlphaNumericChars, - [](char letter) { return FudUtf8::makeUtf8(ExtUtf8Char4{static_cast<utf8>(letter)}); }), - [](auto& utf8Letter) { - bool predicate = false; - auto isPredicateStatus = ext_lib_utf8_is_alphanumeric(&utf8Letter, &predicate); - return isPredicateStatus != ExtSuccess || predicate; - })); - ASSERT_FALSE(allOf( - map(nonAlphaNumericChars, - [](char letter) { return FudUtf8::makeUtf8(ExtUtf8Char4{static_cast<utf8>(letter)}); }), - [](auto& utf8Letter) { - bool predicate = false; - auto isPredicateStatus = ext_lib_utf8_is_alphanumeric(&utf8Letter, &predicate); - return isPredicateStatus == ExtInvalidInput && !predicate; - })); -#endif + ASSERT_FALSE(utf8IsAlphanumeric(FudUtf8{Ascii{invalidAscii}})); + + auto iotaGenerator = invalidAsciiGenerator(); + auto generator = generate( + []() { return Array<utf8, invalidAsciiSize>{}; }, + [&]() { return iotaGenerator().map([](auto val) { return static_cast<utf8>(val); }); }); + + SpanGenerator<char, alphanumericChars.size()> alphanumericGenerator{alphanumericChars.span()}; + auto utf8AlphanumericGenerator = [&]() { return alphanumericGenerator().map(toUtf8<uint16_t>); }; + ASSERT_TRUE(allOf(utf8AlphanumericGenerator, utf8IsAlphanumeric)); + + SpanGenerator<char> nonAlphanumericGenerator{nonAlphanumericChars.span().takeOkay()}; + auto utf8NonAlphanumericGenerator = [&]() { return nonAlphanumericGenerator().map(toUtf8<uint16_t>); }; + ASSERT_FALSE(anyOf(utf8NonAlphanumericGenerator, utf8IsAlphanumeric)); } -#if 0 TEST(Utf8Test, Utf8IsAlpha) { constexpr size_t numAlphaChars = sizeof(ALPHA_CHARS) - 1; @@ -311,75 +279,29 @@ TEST(Utf8Test, Utf8IsAlpha) Array<char, numAlphaChars> alphaChars{}; copyMem<numAlphaChars>(alphaChars, alphaCharLiteral); - ASSERT_TRUE(allOf(alphaChars, ext_lib_char_is_alpha)); - - auto alphaSetResult{StaticSet<char, numAlphaChars>::makeFromArray(alphaChars)}; - ASSERT_TRUE(alphaSetResult.isOkay()); - auto alphaSet{std::move(alphaSetResult.getOkay())}; + ASSERT_TRUE(allOf(alphaChars.span(), charIsAlpha)); - constexpr size_t numNonAlphaChars = validAsciiSize - numAlphaChars; - FixedVector<char, numNonAlphaChars> nonAlphaChars{}; + constexpr size_t numNonAlphanumericChars = validAsciiSize - numAlphaChars; + Vector<char> nonAlphaChars{}; for (char idx = 0; idx < INT8_MAX; ++idx) { - if (!alphaSet.isKey(idx)) { - ASSERT_TRUE(nonAlphaChars.pushBack(idx)); + if (!charIsAlphanumeric(idx)) { + ASSERT_EQ(nonAlphaChars.pushBack(idx), FudStatus::Success); } } - ASSERT_FALSE(anyOf(nonAlphaChars, ext_lib_char_is_alpha)); - - auto invalidAsciiChars = generateInvalidAsciiChars(); - ASSERT_FALSE(anyOf(invalidAsciiChars, ext_lib_char_is_alpha)); - - ASSERT_TRUE(allOf( - map(alphaChars, [](auto input) { return ExtUtf8Char4{static_cast<utf8>(input)}; }), - ext_lib_char4_is_alpha)); - ASSERT_FALSE(anyOf( - map(nonAlphaChars, [](auto input) { return ExtUtf8Char4{static_cast<utf8>(input)}; }), - ext_lib_char4_is_alpha)); - ASSERT_FALSE(anyOf( - map(invalidAsciiChars, [](auto input) { return ExtUtf8Char4{static_cast<utf8>(input)}; }), - ext_lib_char4_is_alpha)); - - ASSERT_TRUE(allOf( - map(alphaChars, [](auto input) { return ExtUtf8Char4{static_cast<utf8>(input)}; }), - ext_lib_char4_is_alpha)); - ASSERT_FALSE(anyOf( - map(nonAlphaChars, [](auto input) { return ExtUtf8Char4{static_cast<utf8>(input)}; }), - ext_lib_char4_is_alpha)); - ASSERT_FALSE(anyOf( - map(invalidAsciiChars, [](auto input) { return ExtUtf8Char4{static_cast<utf8>(input)}; }), - ext_lib_char4_is_alpha)); - - Utf8 utf8{invalidAscii}; - bool isAscii = false; - ASSERT_EQ(ext_lib_utf8_is_alpha(nullptr, nullptr), ExtNullPointer); - ASSERT_EQ(ext_lib_utf8_is_alpha(&utf8, nullptr), ExtNullPointer); - ASSERT_EQ(ext_lib_utf8_is_alpha(nullptr, &isAscii), ExtNullPointer); - ASSERT_EQ(ext_lib_utf8_is_alpha(&utf8, &isAscii), ExtInvalidInput); - ASSERT_FALSE(isAscii); - - ASSERT_TRUE(allOf( - map(alphaChars, [](char letter) { return FudUtf8::makeUtf8(ExtUtf8Char4{static_cast<utf8>(letter)}); }), - [](auto& utf8Letter) { - bool predicate = false; - auto isPredicateStatus = ext_lib_utf8_is_alpha(&utf8Letter, &predicate); - return isPredicateStatus == ExtSuccess && predicate; - })); - ASSERT_FALSE(anyOf( - map(nonAlphaChars, - [](char letter) { return FudUtf8::makeUtf8(ExtUtf8Char4{static_cast<utf8>(letter)}); }), - [](auto& utf8Letter) { - bool predicate = false; - auto isPredicateStatus = ext_lib_utf8_is_alpha(&utf8Letter, &predicate); - return isPredicateStatus != ExtSuccess || predicate; - })); - ASSERT_FALSE(allOf( - map(nonAlphaChars, - [](char letter) { return FudUtf8::makeUtf8(ExtUtf8Char4{static_cast<utf8>(letter)}); }), - [](auto& utf8Letter) { - bool predicate = false; - auto isPredicateStatus = ext_lib_utf8_is_alpha(&utf8Letter, &predicate); - return isPredicateStatus == ExtInvalidInput && !predicate; - })); + ASSERT_FALSE(anyOf(nonAlphaChars.span().takeOkay(), charIsAlpha)); + + auto invalidAsciiChars = invalidAsciiGenerator(); + ASSERT_FALSE(anyOf(invalidAsciiChars, charIsAlpha)); + + ASSERT_FALSE(utf8IsAlpha(FudUtf8{Ascii{invalidAscii}})); + + SpanGenerator<char, alphaChars.size()> alphaGenerator{alphaChars.span()}; + auto utf8AlphaGenerator = [&]() { return alphaGenerator().map(toUtf8<uint16_t>); }; + ASSERT_TRUE(allOf(utf8AlphaGenerator, utf8IsAlpha)); + + SpanGenerator<char> nonAlphaGenerator{nonAlphaChars.span().takeOkay()}; + auto utf8NonAlphaGenerator = [&]() { return nonAlphaGenerator().map(toUtf8<uint16_t>); }; + ASSERT_FALSE(anyOf(utf8NonAlphaGenerator, utf8IsAlpha)); } TEST(Utf8Test, Utf8IsLower) @@ -389,75 +311,29 @@ TEST(Utf8Test, Utf8IsLower) Array<char, numLowerChars> lowerChars{}; copyMem<numLowerChars>(lowerChars, lowerCharLiteral); - ASSERT_TRUE(allOf(lowerChars, ext_lib_char_is_lowercase)); - - auto lowerSetResult{StaticSet<char, numLowerChars>::makeFromArray(lowerChars)}; - ASSERT_TRUE(lowerSetResult.isOkay()); - auto lowerSet{std::move(lowerSetResult.getOkay())}; + ASSERT_TRUE(allOf(lowerChars.span(), charIsLowercase)); constexpr size_t numNonLowerChars = validAsciiSize - numLowerChars; - FixedVector<char, numNonLowerChars> nonLowerChars{}; + Vector<char> nonLowerChars{}; for (char idx = 0; idx < INT8_MAX; ++idx) { - if (!lowerSet.isKey(idx)) { - ASSERT_TRUE(nonLowerChars.pushBack(idx)); + if (!charIsLowercase(idx)) { + ASSERT_EQ(nonLowerChars.pushBack(idx), FudStatus::Success); } } - ASSERT_FALSE(anyOf(nonLowerChars, ext_lib_char_is_lowercase)); - - auto invalidAsciiChars = generateInvalidAsciiChars(); - ASSERT_FALSE(anyOf(invalidAsciiChars, ext_lib_char_is_lowercase)); - - ASSERT_TRUE(allOf( - map(lowerChars, [](auto input) { return ExtUtf8Char4{static_cast<utf8>(input)}; }), - ext_lib_char4_is_lowercase)); - ASSERT_FALSE(anyOf( - map(nonLowerChars, [](auto input) { return ExtUtf8Char4{static_cast<utf8>(input)}; }), - ext_lib_char4_is_lowercase)); - ASSERT_FALSE(anyOf( - map(invalidAsciiChars, [](auto input) { return ExtUtf8Char4{static_cast<utf8>(input)}; }), - ext_lib_char4_is_lowercase)); - - ASSERT_TRUE(allOf( - map(lowerChars, [](auto input) { return ExtUtf8Char4{static_cast<utf8>(input)}; }), - ext_lib_char4_is_lowercase)); - ASSERT_FALSE(anyOf( - map(nonLowerChars, [](auto input) { return ExtUtf8Char4{static_cast<utf8>(input)}; }), - ext_lib_char4_is_lowercase)); - ASSERT_FALSE(anyOf( - map(invalidAsciiChars, [](auto input) { return ExtUtf8Char4{static_cast<utf8>(input)}; }), - ext_lib_char4_is_lowercase)); - - Utf8 utf8{invalidAscii}; - bool isAscii = false; - ASSERT_EQ(ext_lib_utf8_is_lowercase(nullptr, nullptr), ExtNullPointer); - ASSERT_EQ(ext_lib_utf8_is_lowercase(&utf8, nullptr), ExtNullPointer); - ASSERT_EQ(ext_lib_utf8_is_lowercase(nullptr, &isAscii), ExtNullPointer); - ASSERT_EQ(ext_lib_utf8_is_lowercase(&utf8, &isAscii), ExtInvalidInput); - ASSERT_FALSE(isAscii); - - ASSERT_TRUE(allOf( - map(lowerChars, [](char letter) { return FudUtf8::makeUtf8(ExtUtf8Char4{static_cast<utf8>(letter)}); }), - [](auto& utf8Letter) { - bool predicate = false; - auto isPredicateStatus = ext_lib_utf8_is_lowercase(&utf8Letter, &predicate); - return isPredicateStatus == ExtSuccess && predicate; - })); - ASSERT_FALSE(anyOf( - map(nonLowerChars, - [](char letter) { return FudUtf8::makeUtf8(ExtUtf8Char4{static_cast<utf8>(letter)}); }), - [](auto& utf8Letter) { - bool predicate = false; - auto isPredicateStatus = ext_lib_utf8_is_lowercase(&utf8Letter, &predicate); - return isPredicateStatus != ExtSuccess || predicate; - })); - ASSERT_FALSE(allOf( - map(nonLowerChars, - [](char letter) { return FudUtf8::makeUtf8(ExtUtf8Char4{static_cast<utf8>(letter)}); }), - [](auto& utf8Letter) { - bool predicate = false; - auto isPredicateStatus = ext_lib_utf8_is_lowercase(&utf8Letter, &predicate); - return isPredicateStatus == ExtInvalidInput && !predicate; - })); + ASSERT_FALSE(anyOf(nonLowerChars.span().takeOkay(), charIsLowercase)); + + auto invalidAsciiChars = invalidAsciiGenerator(); + ASSERT_FALSE(anyOf(invalidAsciiChars, charIsLowercase)); + + ASSERT_FALSE(utf8IsLowercase(FudUtf8{Ascii{invalidAscii}})); + + SpanGenerator<char, lowerChars.size()> lowerGenerator{lowerChars.span()}; + auto utf8LowerGenerator = [&]() { return lowerGenerator().map(toUtf8<uint16_t>); }; + ASSERT_TRUE(allOf(utf8LowerGenerator, utf8IsLowercase)); + + SpanGenerator<char> nonLowerGenerator{nonLowerChars.span().takeOkay()}; + auto utf8NonLowerGenerator = [&]() { return nonLowerGenerator().map(toUtf8<uint16_t>); }; + ASSERT_FALSE(anyOf(utf8NonLowerGenerator, utf8IsLowercase)); } TEST(Utf8Test, Utf8IsUpper) @@ -467,153 +343,61 @@ TEST(Utf8Test, Utf8IsUpper) Array<char, numUpperChars> upperChars{}; copyMem<numUpperChars>(upperChars, upperCharLiteral); - ASSERT_TRUE(allOf(upperChars, ext_lib_char_is_uppercase)); - - auto upperSetResult{StaticSet<char, numUpperChars>::makeFromArray(upperChars)}; - ASSERT_TRUE(upperSetResult.isOkay()); - auto upperSet{std::move(upperSetResult.getOkay())}; + ASSERT_TRUE(allOf(upperChars.span(), charIsUppercase)); constexpr size_t numNonUpperChars = validAsciiSize - numUpperChars; - FixedVector<char, numNonUpperChars> nonUpperChars{}; + Vector<char> nonUpperChars{}; for (char idx = 0; idx < INT8_MAX; ++idx) { - if (!upperSet.isKey(idx)) { - ASSERT_TRUE(nonUpperChars.pushBack(idx)); + if (!charIsUppercase(idx)) { + ASSERT_EQ(nonUpperChars.pushBack(idx), FudStatus::Success); } } - ASSERT_FALSE(anyOf(nonUpperChars, ext_lib_char_is_uppercase)); - - auto invalidAsciiChars = generateInvalidAsciiChars(); - ASSERT_FALSE(anyOf(invalidAsciiChars, ext_lib_char_is_uppercase)); - - ASSERT_TRUE(allOf( - map(upperChars, [](auto input) { return ExtUtf8Char4{static_cast<utf8>(input)}; }), - ext_lib_char4_is_uppercase)); - ASSERT_FALSE(anyOf( - map(nonUpperChars, [](auto input) { return ExtUtf8Char4{static_cast<utf8>(input)}; }), - ext_lib_char4_is_uppercase)); - ASSERT_FALSE(anyOf( - map(invalidAsciiChars, [](auto input) { return ExtUtf8Char4{static_cast<utf8>(input)}; }), - ext_lib_char4_is_uppercase)); - - ASSERT_TRUE(allOf( - map(upperChars, [](auto input) { return ExtUtf8Char4{static_cast<utf8>(input)}; }), - ext_lib_char4_is_uppercase)); - ASSERT_FALSE(anyOf( - map(nonUpperChars, [](auto input) { return ExtUtf8Char4{static_cast<utf8>(input)}; }), - ext_lib_char4_is_uppercase)); - ASSERT_FALSE(anyOf( - map(invalidAsciiChars, [](auto input) { return ExtUtf8Char4{static_cast<utf8>(input)}; }), - ext_lib_char4_is_uppercase)); - - Utf8 utf8{invalidAscii}; - bool isAscii = false; - ASSERT_EQ(ext_lib_utf8_is_uppercase(nullptr, nullptr), ExtNullPointer); - ASSERT_EQ(ext_lib_utf8_is_uppercase(&utf8, nullptr), ExtNullPointer); - ASSERT_EQ(ext_lib_utf8_is_uppercase(nullptr, &isAscii), ExtNullPointer); - ASSERT_EQ(ext_lib_utf8_is_uppercase(&utf8, &isAscii), ExtInvalidInput); - ASSERT_FALSE(isAscii); - - ASSERT_TRUE(allOf( - map(upperChars, [](char letter) { return FudUtf8::makeUtf8(ExtUtf8Char4{static_cast<utf8>(letter)}); }), - [](auto& utf8Letter) { - bool predicate = false; - auto isPredicateStatus = ext_lib_utf8_is_uppercase(&utf8Letter, &predicate); - return isPredicateStatus == ExtSuccess && predicate; - })); - ASSERT_FALSE(anyOf( - map(nonUpperChars, - [](char letter) { return FudUtf8::makeUtf8(ExtUtf8Char4{static_cast<utf8>(letter)}); }), - [](auto& utf8Letter) { - bool predicate = false; - auto isPredicateStatus = ext_lib_utf8_is_uppercase(&utf8Letter, &predicate); - return isPredicateStatus != ExtSuccess || predicate; - })); - ASSERT_FALSE(allOf( - map(nonUpperChars, - [](char letter) { return FudUtf8::makeUtf8(ExtUtf8Char4{static_cast<utf8>(letter)}); }), - [](auto& utf8Letter) { - bool predicate = false; - auto isPredicateStatus = ext_lib_utf8_is_uppercase(&utf8Letter, &predicate); - return isPredicateStatus == ExtInvalidInput && !predicate; - })); + ASSERT_FALSE(anyOf(nonUpperChars.span().takeOkay(), charIsUppercase)); + + auto invalidAsciiChars = invalidAsciiGenerator(); + ASSERT_FALSE(anyOf(invalidAsciiChars, charIsUppercase)); + + ASSERT_FALSE(utf8IsUppercase(FudUtf8{Ascii{invalidAscii}})); + + SpanGenerator<char, upperChars.size()> upperGenerator{upperChars.span()}; + auto utf8UpperGenerator = [&]() { return upperGenerator().map(toUtf8<uint16_t>); }; + ASSERT_TRUE(allOf(utf8UpperGenerator, utf8IsUppercase)); + + SpanGenerator<char> nonUpperGenerator{nonUpperChars.span().takeOkay()}; + auto utf8NonUpperGenerator = [&]() { return nonUpperGenerator().map(toUtf8<uint16_t>); }; + ASSERT_FALSE(anyOf(utf8NonUpperGenerator, utf8IsUppercase)); } TEST(Utf8Test, Utf8IsDigit) { constexpr size_t numDigitChars = 10; - Array<char, numDigitChars + 1> digitCharLiteral{"0123456789"}; + Array<char, numDigitChars + 1> digitCharLiteral{DECIMAL_CHARS}; Array<char, numDigitChars> digitChars{}; copyMem<numDigitChars>(digitChars, digitCharLiteral); - ASSERT_TRUE(allOf(digitChars, ext_lib_char_is_digit)); - - auto digitSetResult{StaticSet<char, numDigitChars>::makeFromArray(digitChars)}; - ASSERT_TRUE(digitSetResult.isOkay()); - auto digitSet{std::move(digitSetResult.getOkay())}; + ASSERT_TRUE(allOf(digitChars.span(), charIsDigit)); constexpr size_t numNonDigitChars = validAsciiSize - numDigitChars; - FixedVector<char, numNonDigitChars> nonDigitChars{}; + Vector<char> nonDigitChars{}; for (char idx = 0; idx < INT8_MAX; ++idx) { - if (!digitSet.isKey(idx)) { - ASSERT_TRUE(nonDigitChars.pushBack(idx)); + if (!charIsDigit(idx)) { + ASSERT_EQ(nonDigitChars.pushBack(idx), FudStatus::Success); } } - ASSERT_FALSE(anyOf(nonDigitChars, ext_lib_char_is_digit)); - - auto invalidAsciiChars = generateInvalidAsciiChars(); - ASSERT_FALSE(anyOf(invalidAsciiChars, ext_lib_char_is_digit)); - - ASSERT_TRUE(allOf( - map(digitChars, [](auto input) { return ExtUtf8Char4{static_cast<utf8>(input)}; }), - ext_lib_char4_is_digit)); - ASSERT_FALSE(anyOf( - map(nonDigitChars, [](auto input) { return ExtUtf8Char4{static_cast<utf8>(input)}; }), - ext_lib_char4_is_digit)); - ASSERT_FALSE(anyOf( - map(invalidAsciiChars, [](auto input) { return ExtUtf8Char4{static_cast<utf8>(input)}; }), - ext_lib_char4_is_digit)); - - ASSERT_TRUE(allOf( - map(digitChars, [](auto input) { return ExtUtf8Char4{static_cast<utf8>(input)}; }), - ext_lib_char4_is_digit)); - ASSERT_FALSE(anyOf( - map(nonDigitChars, [](auto input) { return ExtUtf8Char4{static_cast<utf8>(input)}; }), - ext_lib_char4_is_digit)); - ASSERT_FALSE(anyOf( - map(invalidAsciiChars, [](auto input) { return ExtUtf8Char4{static_cast<utf8>(input)}; }), - ext_lib_char4_is_digit)); - - Utf8 utf8{invalidAscii}; - bool isAscii = false; - ASSERT_EQ(ext_lib_utf8_is_digit(nullptr, nullptr), ExtNullPointer); - ASSERT_EQ(ext_lib_utf8_is_digit(&utf8, nullptr), ExtNullPointer); - ASSERT_EQ(ext_lib_utf8_is_digit(nullptr, &isAscii), ExtNullPointer); - ASSERT_EQ(ext_lib_utf8_is_digit(&utf8, &isAscii), ExtInvalidInput); - ASSERT_FALSE(isAscii); - - ASSERT_TRUE(allOf( - map(digitChars, [](char letter) { return FudUtf8::makeUtf8(ExtUtf8Char4{static_cast<utf8>(letter)}); }), - [](auto& utf8Letter) { - bool predicate = false; - auto isPredicateStatus = ext_lib_utf8_is_digit(&utf8Letter, &predicate); - return isPredicateStatus == ExtSuccess && predicate; - })); - ASSERT_FALSE(anyOf( - map(nonDigitChars, - [](char letter) { return FudUtf8::makeUtf8(ExtUtf8Char4{static_cast<utf8>(letter)}); }), - [](auto& utf8Letter) { - bool predicate = false; - auto isPredicateStatus = ext_lib_utf8_is_digit(&utf8Letter, &predicate); - return isPredicateStatus != ExtSuccess || predicate; - })); - ASSERT_FALSE(allOf( - map(nonDigitChars, - [](char letter) { return FudUtf8::makeUtf8(ExtUtf8Char4{static_cast<utf8>(letter)}); }), - [](auto& utf8Letter) { - bool predicate = false; - auto isPredicateStatus = ext_lib_utf8_is_digit(&utf8Letter, &predicate); - return isPredicateStatus == ExtInvalidInput && !predicate; - })); + ASSERT_FALSE(anyOf(nonDigitChars.span().takeOkay(), charIsDigit)); + + auto invalidAsciiChars = invalidAsciiGenerator(); + ASSERT_FALSE(anyOf(invalidAsciiChars, charIsDigit)); + + ASSERT_FALSE(utf8IsDigit(FudUtf8{Ascii{invalidAscii}})); + + SpanGenerator<char, digitChars.size()> digitGenerator{digitChars.span()}; + auto utf8DigitGenerator = [&]() { return digitGenerator().map(toUtf8<uint16_t>); }; + ASSERT_TRUE(allOf(utf8DigitGenerator, utf8IsDigit)); + + SpanGenerator<char> nonDigitGenerator{nonDigitChars.span().takeOkay()}; + auto utf8NonDigitGenerator = [&]() { return nonDigitGenerator().map(toUtf8<uint16_t>); }; + ASSERT_FALSE(anyOf(utf8NonDigitGenerator, utf8IsDigit)); } TEST(Utf8Test, Utf8IsHexDigit) @@ -623,147 +407,61 @@ TEST(Utf8Test, Utf8IsHexDigit) Array<char, numHexDigitChars> hexDigitChars{}; copyMem<numHexDigitChars>(hexDigitChars, hexDigitCharLiteral); - ASSERT_TRUE(allOf(hexDigitChars, ext_lib_char_is_hex_digit)); - - auto hexDigitSetResult{StaticSet<char, numHexDigitChars>::makeFromArray(hexDigitChars)}; - ASSERT_TRUE(hexDigitSetResult.isOkay()); - auto hexDigitSet{std::move(hexDigitSetResult.getOkay())}; + ASSERT_TRUE(allOf(hexDigitChars.span(), charIsHexDigit)); constexpr size_t numNonHexDigitChars = validAsciiSize - numHexDigitChars; - FixedVector<char, numNonHexDigitChars> nonHexDigitChars{}; + Vector<char> nonHexDigitChars{}; for (char idx = 0; idx < INT8_MAX; ++idx) { - if (!hexDigitSet.isKey(idx)) { - ASSERT_TRUE(nonHexDigitChars.pushBack(idx)); + if (!charIsHexDigit(idx)) { + ASSERT_EQ(nonHexDigitChars.pushBack(idx), FudStatus::Success); } } - ASSERT_FALSE(anyOf(nonHexDigitChars, ext_lib_char_is_hex_digit)); - - auto invalidAsciiChars = generateInvalidAsciiChars(); - ASSERT_FALSE(anyOf(invalidAsciiChars, ext_lib_char_is_hex_digit)); - - ASSERT_TRUE(allOf( - map(hexDigitChars, [](auto input) { return ExtUtf8Char4{static_cast<utf8>(input)}; }), - ext_lib_char4_is_hex_digit)); - ASSERT_FALSE(anyOf( - map(nonHexDigitChars, [](auto input) { return ExtUtf8Char4{static_cast<utf8>(input)}; }), - ext_lib_char4_is_hex_digit)); - ASSERT_FALSE(anyOf( - map(invalidAsciiChars, [](auto input) { return ExtUtf8Char4{static_cast<utf8>(input)}; }), - ext_lib_char4_is_hex_digit)); - - ASSERT_TRUE(allOf( - map(hexDigitChars, [](auto input) { return ExtUtf8Char4{static_cast<utf8>(input)}; }), - ext_lib_char4_is_hex_digit)); - ASSERT_FALSE(anyOf( - map(nonHexDigitChars, [](auto input) { return ExtUtf8Char4{static_cast<utf8>(input)}; }), - ext_lib_char4_is_hex_digit)); - ASSERT_FALSE(anyOf( - map(invalidAsciiChars, [](auto input) { return ExtUtf8Char4{static_cast<utf8>(input)}; }), - ext_lib_char4_is_hex_digit)); - - Utf8 utf8{invalidAscii}; - bool isAscii = false; - ASSERT_EQ(ext_lib_utf8_is_hex_digit(nullptr, nullptr), ExtNullPointer); - ASSERT_EQ(ext_lib_utf8_is_hex_digit(&utf8, nullptr), ExtNullPointer); - ASSERT_EQ(ext_lib_utf8_is_hex_digit(nullptr, &isAscii), ExtNullPointer); - ASSERT_EQ(ext_lib_utf8_is_hex_digit(&utf8, &isAscii), ExtInvalidInput); - ASSERT_FALSE(isAscii); - - ASSERT_TRUE(allOf( - map(hexDigitChars, - [](char letter) { return FudUtf8::makeUtf8(ExtUtf8Char4{static_cast<utf8>(letter)}); }), - [](auto& utf8Letter) { - bool predicate = false; - auto isPredicateStatus = ext_lib_utf8_is_hex_digit(&utf8Letter, &predicate); - return isPredicateStatus == ExtSuccess && predicate; - })); - ASSERT_FALSE(anyOf( - map(nonHexDigitChars, - [](char letter) { return FudUtf8::makeUtf8(ExtUtf8Char4{static_cast<utf8>(letter)}); }), - [](auto& utf8Letter) { - bool predicate = false; - auto isPredicateStatus = ext_lib_utf8_is_hex_digit(&utf8Letter, &predicate); - return isPredicateStatus != ExtSuccess || predicate; - })); - ASSERT_FALSE(allOf( - map(nonHexDigitChars, - [](char letter) { return FudUtf8::makeUtf8(ExtUtf8Char4{static_cast<utf8>(letter)}); }), - [](auto& utf8Letter) { - bool predicate = false; - auto isPredicateStatus = ext_lib_utf8_is_hex_digit(&utf8Letter, &predicate); - return isPredicateStatus == ExtInvalidInput && !predicate; - })); + ASSERT_FALSE(anyOf(nonHexDigitChars.span().takeOkay(), charIsHexDigit)); + + auto invalidAsciiChars = invalidAsciiGenerator(); + ASSERT_FALSE(anyOf(invalidAsciiChars, charIsHexDigit)); + + ASSERT_FALSE(utf8IsHexDigit(FudUtf8{Ascii{invalidAscii}})); + + SpanGenerator<char, hexDigitChars.size()> hexDigitGenerator{hexDigitChars.span()}; + auto utf8HexDigitGenerator = [&]() { return hexDigitGenerator().map(toUtf8<uint16_t>); }; + ASSERT_TRUE(allOf(utf8HexDigitGenerator, utf8IsHexDigit)); + + SpanGenerator<char> nonHexDigitGenerator{nonHexDigitChars.span().takeOkay()}; + auto utf8NonHexDigitGenerator = [&]() { return nonHexDigitGenerator().map(toUtf8<uint16_t>); }; + ASSERT_FALSE(anyOf(utf8NonHexDigitGenerator, utf8IsHexDigit)); } TEST(Utf8Test, Utf8IsControl) { - auto controlChars = generateIndexArray<Array, char, numControlChars>([](int idx) { return static_cast<char>(idx); }); + Iota<char> controlArrayGenerator{0, 1, numControlChars}; + auto controlChars = generate([]() { return Array<char, numControlChars>{}; }, controlArrayGenerator); constexpr const char deleteChar = 0x7F; controlChars.back() = deleteChar; - ASSERT_TRUE(allOf(controlChars, ext_lib_char_is_control)); - - constexpr size_t numNonControlChars = 256 - numControlChars; - auto nonControlChars = generateIndexArray<Array, char, numControlChars>([](int idx) { - return static_cast<char>(idx + printableCharOffset); - }); - ASSERT_FALSE(anyOf(nonControlChars, ext_lib_char_is_control)); - - auto invalidAsciiChars = generateInvalidAsciiChars(); - ASSERT_FALSE(anyOf(invalidAsciiChars, ext_lib_char_is_control)); - - ASSERT_TRUE(allOf( - map(controlChars, [](auto input) { return ExtUtf8Char4{static_cast<utf8>(input)}; }), - ext_lib_char4_is_control)); - ASSERT_FALSE(anyOf( - map(nonControlChars, [](auto input) { return ExtUtf8Char4{static_cast<utf8>(input)}; }), - ext_lib_char4_is_control)); - ASSERT_FALSE(anyOf( - map(invalidAsciiChars, [](auto input) { return ExtUtf8Char4{static_cast<utf8>(input)}; }), - ext_lib_char4_is_control)); - - ASSERT_TRUE(allOf( - map(controlChars, [](auto input) { return ExtUtf8Char4{static_cast<utf8>(input)}; }), - ext_lib_char4_is_control)); - ASSERT_FALSE(anyOf( - map(nonControlChars, [](auto input) { return ExtUtf8Char4{static_cast<utf8>(input)}; }), - ext_lib_char4_is_control)); - ASSERT_FALSE(anyOf( - map(invalidAsciiChars, [](auto input) { return ExtUtf8Char4{static_cast<utf8>(input)}; }), - ext_lib_char4_is_control)); - - Utf8 utf8{invalidAscii}; - bool isAscii = false; - ASSERT_EQ(ext_lib_utf8_is_control(nullptr, nullptr), ExtNullPointer); - ASSERT_EQ(ext_lib_utf8_is_control(&utf8, nullptr), ExtNullPointer); - ASSERT_EQ(ext_lib_utf8_is_control(nullptr, &isAscii), ExtNullPointer); - ASSERT_EQ(ext_lib_utf8_is_control(&utf8, &isAscii), ExtInvalidInput); - ASSERT_FALSE(isAscii); - - ASSERT_TRUE(allOf( - map(controlChars, - [](char letter) { return FudUtf8::makeUtf8(ExtUtf8Char4{static_cast<utf8>(letter)}); }), - [](auto& utf8Letter) { - bool predicate = false; - auto isPredicateStatus = ext_lib_utf8_is_control(&utf8Letter, &predicate); - return isPredicateStatus == ExtSuccess && predicate; - })); - ASSERT_FALSE(anyOf( - map(nonControlChars, - [](char letter) { return FudUtf8::makeUtf8(ExtUtf8Char4{static_cast<utf8>(letter)}); }), - [](auto& utf8Letter) { - bool predicate = false; - auto isPredicateStatus = ext_lib_utf8_is_control(&utf8Letter, &predicate); - return isPredicateStatus != ExtSuccess || predicate; - })); - ASSERT_FALSE(allOf( - map(nonControlChars, - [](char letter) { return FudUtf8::makeUtf8(ExtUtf8Char4{static_cast<utf8>(letter)}); }), - [](auto& utf8Letter) { - bool predicate = false; - auto isPredicateStatus = ext_lib_utf8_is_control(&utf8Letter, &predicate); - return isPredicateStatus == ExtInvalidInput && !predicate; - })); + ASSERT_TRUE(allOf(controlChars.span(), charIsControl)); + + constexpr size_t numNonControlChars = INT8_MAX + 1 - numControlChars; + Vector<char> nonControlChars{}; + ASSERT_EQ(nonControlChars.reserve(numNonControlChars), FudStatus::Success); + for (auto idx = numControlChars - 1; idx < deleteChar; ++idx) { + ASSERT_EQ(nonControlChars.pushBack(idx), FudStatus::Success); + } + ASSERT_FALSE(anyOf(nonControlChars.span().takeOkay(), charIsControl)); + ASSERT_TRUE(allOf(nonControlChars.span().takeOkay(), charIsAscii)); + + auto invalidAsciiChars = invalidAsciiGenerator(); + ASSERT_FALSE(anyOf(invalidAsciiChars, charIsControl)); + + ASSERT_FALSE(utf8IsControl(FudUtf8{Ascii{invalidAscii}})); + + SpanGenerator<char, controlChars.size()> controlGenerator{controlChars.span()}; + auto utf8ControlGenerator = [&]() { return controlGenerator().map(toUtf8<uint16_t>); }; + ASSERT_TRUE(allOf(utf8ControlGenerator, utf8IsControl)); + + SpanGenerator<char> nonControlGenerator{nonControlChars.span().takeOkay()}; + auto utf8NonControlGenerator = [&]() { return nonControlGenerator().map(toUtf8<uint16_t>); }; + ASSERT_FALSE(anyOf(utf8NonControlGenerator, utf8IsControl)); } TEST(Utf8Test, Utf8IsGraphical) @@ -773,311 +471,137 @@ TEST(Utf8Test, Utf8IsGraphical) Array<char, numGraphicalChars> graphicalChars{}; copyMem<numGraphicalChars>(graphicalChars, graphicalCharLiteral); - ASSERT_TRUE(allOf(graphicalChars, ext_lib_char_is_graphical)); - - auto graphicalSetResult{StaticSet<char, numGraphicalChars>::makeFromArray(graphicalChars)}; - ASSERT_TRUE(graphicalSetResult.isOkay()); - auto graphicalSet{std::move(graphicalSetResult.getOkay())}; + ASSERT_TRUE(allOf(graphicalChars.span(), charIsGraphical)); constexpr size_t numNonGraphicalChars = validAsciiSize - numGraphicalChars; - FixedVector<char, numNonGraphicalChars> nonGraphicalChars{}; - for (char idx = 0; idx < INT8_MAX; ++idx) { - if (!graphicalSet.isKey(idx)) { - ASSERT_TRUE(nonGraphicalChars.pushBack(idx)); + Vector<char> nonGraphicalChars{}; + ASSERT_EQ(nonGraphicalChars.reserve(numNonGraphicalChars), FudStatus::Success); + for (uint8_t idx = 0; idx < INT8_MAX + 1; ++idx) { + if (!charIsGraphical(static_cast<char>(idx))) { + ASSERT_EQ(nonGraphicalChars.pushBack(static_cast<char>(idx)), FudStatus::Success); } } - ASSERT_FALSE(anyOf(nonGraphicalChars, ext_lib_char_is_graphical)); - - auto invalidAsciiChars = generateInvalidAsciiChars(); - ASSERT_FALSE(anyOf(invalidAsciiChars, ext_lib_char_is_graphical)); - - ASSERT_TRUE(allOf( - map(graphicalChars, [](auto input) { return ExtUtf8Char4{static_cast<utf8>(input)}; }), - ext_lib_char4_is_graphical)); - ASSERT_FALSE(anyOf( - map(nonGraphicalChars, [](auto input) { return ExtUtf8Char4{static_cast<utf8>(input)}; }), - ext_lib_char4_is_graphical)); - ASSERT_FALSE(anyOf( - map(invalidAsciiChars, [](auto input) { return ExtUtf8Char4{static_cast<utf8>(input)}; }), - ext_lib_char4_is_graphical)); - - ASSERT_TRUE(allOf( - map(graphicalChars, [](auto input) { return ExtUtf8Char4{static_cast<utf8>(input)}; }), - ext_lib_char4_is_graphical)); - ASSERT_FALSE(anyOf( - map(nonGraphicalChars, [](auto input) { return ExtUtf8Char4{static_cast<utf8>(input)}; }), - ext_lib_char4_is_graphical)); - ASSERT_FALSE(anyOf( - map(invalidAsciiChars, [](auto input) { return ExtUtf8Char4{static_cast<utf8>(input)}; }), - ext_lib_char4_is_graphical)); - - Utf8 utf8{invalidAscii}; - bool isAscii = false; - ASSERT_EQ(ext_lib_utf8_is_graphical(nullptr, nullptr), ExtNullPointer); - ASSERT_EQ(ext_lib_utf8_is_graphical(&utf8, nullptr), ExtNullPointer); - ASSERT_EQ(ext_lib_utf8_is_graphical(nullptr, &isAscii), ExtNullPointer); - ASSERT_EQ(ext_lib_utf8_is_graphical(&utf8, &isAscii), ExtInvalidInput); - ASSERT_FALSE(isAscii); - - ASSERT_TRUE(allOf( - map(graphicalChars, - [](char letter) { return FudUtf8::makeUtf8(ExtUtf8Char4{static_cast<utf8>(letter)}); }), - [](auto& utf8Letter) { - bool predicate = false; - auto isPredicateStatus = ext_lib_utf8_is_graphical(&utf8Letter, &predicate); - return isPredicateStatus == ExtSuccess && predicate; - })); - ASSERT_FALSE(anyOf( - map(nonGraphicalChars, - [](char letter) { return FudUtf8::makeUtf8(ExtUtf8Char4{static_cast<utf8>(letter)}); }), - [](auto& utf8Letter) { - bool predicate = false; - auto isPredicateStatus = ext_lib_utf8_is_graphical(&utf8Letter, &predicate); - return isPredicateStatus != ExtSuccess || predicate; - })); - ASSERT_FALSE(allOf( - map(nonGraphicalChars, - [](char letter) { return FudUtf8::makeUtf8(ExtUtf8Char4{static_cast<utf8>(letter)}); }), - [](auto& utf8Letter) { - bool predicate = false; - auto isPredicateStatus = ext_lib_utf8_is_graphical(&utf8Letter, &predicate); - return isPredicateStatus == ExtInvalidInput && !predicate; - })); + ASSERT_FALSE(anyOf(nonGraphicalChars.span().takeOkay(), charIsGraphical)); + ASSERT_TRUE(allOf(nonGraphicalChars.span().takeOkay(), charIsAscii)); + ASSERT_EQ(nonGraphicalChars.size() + graphicalChars.size(), INT8_MAX + 1); + + auto invalidAsciiChars = invalidAsciiGenerator(); + ASSERT_FALSE(anyOf(invalidAsciiChars, charIsGraphical)); + + ASSERT_FALSE(utf8IsGraphical(FudUtf8{Ascii{invalidAscii}})); + + SpanGenerator<char, graphicalChars.size()> graphicalGenerator{graphicalChars.span()}; + auto utf8GraphicalGenerator = [&]() { return graphicalGenerator().map(toUtf8<uint16_t>); }; + ASSERT_TRUE(allOf(utf8GraphicalGenerator, utf8IsGraphical)); + + SpanGenerator<char> nonGraphicalGenerator{nonGraphicalChars.span().takeOkay()}; + auto utf8NonGraphicalGenerator = [&]() { return nonGraphicalGenerator().map(toUtf8<uint16_t>); }; + ASSERT_FALSE(anyOf(utf8NonGraphicalGenerator, utf8IsGraphical)); } TEST(Utf8Test, Utf8IsSpace) { - constexpr size_t numSpaceChars = sizeof(" \t\v\r\n") - 1; - Array<char, numSpaceChars + 1> spaceCharLiteral{" \t\v\r\n"}; + constexpr size_t numSpaceChars = sizeof(SPACE_CHARS) - 1; + Array<char, numSpaceChars + 1> spaceCharLiteral{SPACE_CHARS}; Array<char, numSpaceChars> spaceChars{}; copyMem<numSpaceChars>(spaceChars, spaceCharLiteral); - ASSERT_TRUE(allOf(spaceChars, ext_lib_char_is_space)); - - auto spaceSetResult{StaticSet<char, numSpaceChars>::makeFromArray(spaceChars)}; - ASSERT_TRUE(spaceSetResult.isOkay()); - auto spaceSet{std::move(spaceSetResult.getOkay())}; + ASSERT_TRUE(allOf(spaceChars.span(), charIsSpace)); constexpr size_t numNonSpaceChars = validAsciiSize - numSpaceChars; - FixedVector<char, numNonSpaceChars> nonSpaceChars{}; - for (char idx = 0; idx < INT8_MAX; ++idx) { - if (!spaceSet.isKey(idx)) { - ASSERT_TRUE(nonSpaceChars.pushBack(idx)); + Vector<char> nonSpaceChars{}; + ASSERT_EQ(nonSpaceChars.reserve(numNonSpaceChars), FudStatus::Success); + for (uint8_t idx = 0; idx < INT8_MAX + 1; ++idx) { + if (!charIsSpace(static_cast<char>(idx))) { + ASSERT_EQ(nonSpaceChars.pushBack(static_cast<char>(idx)), FudStatus::Success); } } - ASSERT_FALSE(anyOf(nonSpaceChars, ext_lib_char_is_space)); - - auto invalidAsciiChars = generateInvalidAsciiChars(); - ASSERT_FALSE(anyOf(invalidAsciiChars, ext_lib_char_is_space)); - - ASSERT_TRUE(allOf( - map(spaceChars, [](auto input) { return ExtUtf8Char4{static_cast<utf8>(input)}; }), - ext_lib_char4_is_space)); - ASSERT_FALSE(anyOf( - map(nonSpaceChars, [](auto input) { return ExtUtf8Char4{static_cast<utf8>(input)}; }), - ext_lib_char4_is_space)); - ASSERT_FALSE(anyOf( - map(invalidAsciiChars, [](auto input) { return ExtUtf8Char4{static_cast<utf8>(input)}; }), - ext_lib_char4_is_space)); - - ASSERT_TRUE(allOf( - map(spaceChars, [](auto input) { return ExtUtf8Char4{static_cast<utf8>(input)}; }), - ext_lib_char4_is_space)); - ASSERT_FALSE(anyOf( - map(nonSpaceChars, [](auto input) { return ExtUtf8Char4{static_cast<utf8>(input)}; }), - ext_lib_char4_is_space)); - ASSERT_FALSE(anyOf( - map(invalidAsciiChars, [](auto input) { return ExtUtf8Char4{static_cast<utf8>(input)}; }), - ext_lib_char4_is_space)); - - Utf8 utf8{invalidAscii}; - bool isAscii = false; - ASSERT_EQ(ext_lib_utf8_is_space(nullptr, nullptr), ExtNullPointer); - ASSERT_EQ(ext_lib_utf8_is_space(&utf8, nullptr), ExtNullPointer); - ASSERT_EQ(ext_lib_utf8_is_space(nullptr, &isAscii), ExtNullPointer); - ASSERT_EQ(ext_lib_utf8_is_space(&utf8, &isAscii), ExtInvalidInput); - ASSERT_FALSE(isAscii); - - ASSERT_TRUE(allOf( - map(spaceChars, [](char letter) { return FudUtf8::makeUtf8(ExtUtf8Char4{static_cast<utf8>(letter)}); }), - [](auto& utf8Letter) { - bool predicate = false; - auto isPredicateStatus = ext_lib_utf8_is_space(&utf8Letter, &predicate); - return isPredicateStatus == ExtSuccess && predicate; - })); - ASSERT_FALSE(anyOf( - map(nonSpaceChars, - [](char letter) { return FudUtf8::makeUtf8(ExtUtf8Char4{static_cast<utf8>(letter)}); }), - [](auto& utf8Letter) { - bool predicate = false; - auto isPredicateStatus = ext_lib_utf8_is_space(&utf8Letter, &predicate); - return isPredicateStatus != ExtSuccess || predicate; - })); - ASSERT_FALSE(allOf( - map(nonSpaceChars, - [](char letter) { return FudUtf8::makeUtf8(ExtUtf8Char4{static_cast<utf8>(letter)}); }), - [](auto& utf8Letter) { - bool predicate = false; - auto isPredicateStatus = ext_lib_utf8_is_space(&utf8Letter, &predicate); - return isPredicateStatus == ExtInvalidInput && !predicate; - })); + ASSERT_FALSE(anyOf(nonSpaceChars.span().takeOkay(), charIsSpace)); + ASSERT_TRUE(allOf(nonSpaceChars.span().takeOkay(), charIsAscii)); + ASSERT_EQ(nonSpaceChars.size() + spaceChars.size(), INT8_MAX + 1); + + auto invalidAsciiChars = invalidAsciiGenerator(); + ASSERT_FALSE(anyOf(invalidAsciiChars, charIsSpace)); + + ASSERT_FALSE(utf8IsSpace(FudUtf8{Ascii{invalidAscii}})); + + SpanGenerator<char, spaceChars.size()> spaceGenerator{spaceChars.span()}; + auto utf8SpaceGenerator = [&]() { return spaceGenerator().map(toUtf8<uint16_t>); }; + ASSERT_TRUE(allOf(utf8SpaceGenerator, utf8IsSpace)); + + SpanGenerator<char> nonSpaceGenerator{nonSpaceChars.span().takeOkay()}; + auto utf8NonSpaceGenerator = [&]() { return nonSpaceGenerator().map(toUtf8<uint16_t>); }; + ASSERT_FALSE(anyOf(utf8NonSpaceGenerator, utf8IsSpace)); } TEST(Utf8Test, Utf8IsBlank) { - constexpr size_t numBlankChars = sizeof(" \t") - 1; - Array<char, numBlankChars + 1> blankCharLiteral{" \t"}; + constexpr size_t numBlankChars = sizeof(BLANK_CHARS) - 1; + Array<char, numBlankChars + 1> blankCharLiteral{BLANK_CHARS}; Array<char, numBlankChars> blankChars{}; copyMem<numBlankChars>(blankChars, blankCharLiteral); - ASSERT_TRUE(allOf(blankChars, ext_lib_char_is_blank)); - - auto blankSetResult{StaticSet<char, numBlankChars>::makeFromArray(blankChars)}; - ASSERT_TRUE(blankSetResult.isOkay()); - auto blankSet{std::move(blankSetResult.getOkay())}; + ASSERT_TRUE(allOf(blankChars.span(), charIsBlank)); constexpr size_t numNonBlankChars = validAsciiSize - numBlankChars; - FixedVector<char, numNonBlankChars> nonBlankChars{}; - for (char idx = 0; idx < INT8_MAX; ++idx) { - if (!blankSet.isKey(idx)) { - ASSERT_TRUE(nonBlankChars.pushBack(idx)); + Vector<char> nonBlankChars{}; + ASSERT_EQ(nonBlankChars.reserve(numNonBlankChars), FudStatus::Success); + for (uint8_t idx = 0; idx < INT8_MAX + 1; ++idx) { + if (!charIsBlank(static_cast<char>(idx))) { + ASSERT_EQ(nonBlankChars.pushBack(static_cast<char>(idx)), FudStatus::Success); } } - ASSERT_FALSE(anyOf(nonBlankChars, ext_lib_char_is_blank)); - - auto invalidAsciiChars = generateInvalidAsciiChars(); - ASSERT_FALSE(anyOf(invalidAsciiChars, ext_lib_char_is_blank)); - - ASSERT_TRUE(allOf( - map(blankChars, [](auto input) { return ExtUtf8Char4{static_cast<utf8>(input)}; }), - ext_lib_char4_is_blank)); - ASSERT_FALSE(anyOf( - map(nonBlankChars, [](auto input) { return ExtUtf8Char4{static_cast<utf8>(input)}; }), - ext_lib_char4_is_blank)); - ASSERT_FALSE(anyOf( - map(invalidAsciiChars, [](auto input) { return ExtUtf8Char4{static_cast<utf8>(input)}; }), - ext_lib_char4_is_blank)); - - ASSERT_TRUE(allOf( - map(blankChars, [](auto input) { return ExtUtf8Char4{static_cast<utf8>(input)}; }), - ext_lib_char4_is_blank)); - ASSERT_FALSE(anyOf( - map(nonBlankChars, [](auto input) { return ExtUtf8Char4{static_cast<utf8>(input)}; }), - ext_lib_char4_is_blank)); - ASSERT_FALSE(anyOf( - map(invalidAsciiChars, [](auto input) { return ExtUtf8Char4{static_cast<utf8>(input)}; }), - ext_lib_char4_is_blank)); - - Utf8 utf8{invalidAscii}; - bool isAscii = false; - ASSERT_EQ(ext_lib_utf8_is_blank(nullptr, nullptr), ExtNullPointer); - ASSERT_EQ(ext_lib_utf8_is_blank(&utf8, nullptr), ExtNullPointer); - ASSERT_EQ(ext_lib_utf8_is_blank(nullptr, &isAscii), ExtNullPointer); - ASSERT_EQ(ext_lib_utf8_is_blank(&utf8, &isAscii), ExtInvalidInput); - ASSERT_FALSE(isAscii); - - ASSERT_TRUE(allOf( - map(blankChars, [](char letter) { return FudUtf8::makeUtf8(ExtUtf8Char4{static_cast<utf8>(letter)}); }), - [](auto& utf8Letter) { - bool predicate = false; - auto isPredicateStatus = ext_lib_utf8_is_blank(&utf8Letter, &predicate); - return isPredicateStatus == ExtSuccess && predicate; - })); - ASSERT_FALSE(anyOf( - map(nonBlankChars, - [](char letter) { return FudUtf8::makeUtf8(ExtUtf8Char4{static_cast<utf8>(letter)}); }), - [](auto& utf8Letter) { - bool predicate = false; - auto isPredicateStatus = ext_lib_utf8_is_blank(&utf8Letter, &predicate); - return isPredicateStatus != ExtSuccess || predicate; - })); - ASSERT_FALSE(allOf( - map(nonBlankChars, - [](char letter) { return FudUtf8::makeUtf8(ExtUtf8Char4{static_cast<utf8>(letter)}); }), - [](auto& utf8Letter) { - bool predicate = false; - auto isPredicateStatus = ext_lib_utf8_is_blank(&utf8Letter, &predicate); - return isPredicateStatus == ExtInvalidInput && !predicate; - })); + ASSERT_FALSE(anyOf(nonBlankChars.span().takeOkay(), charIsBlank)); + ASSERT_TRUE(allOf(nonBlankChars.span().takeOkay(), charIsAscii)); + ASSERT_EQ(nonBlankChars.size() + blankChars.size(), INT8_MAX + 1); + + auto invalidAsciiChars = invalidAsciiGenerator(); + ASSERT_FALSE(anyOf(invalidAsciiChars, charIsBlank)); + + ASSERT_FALSE(utf8IsBlank(FudUtf8{Ascii{invalidAscii}})); + + SpanGenerator<char, blankChars.size()> blankGenerator{blankChars.span()}; + auto utf8BlankGenerator = [&]() { return blankGenerator().map(toUtf8<uint16_t>); }; + ASSERT_TRUE(allOf(utf8BlankGenerator, utf8IsBlank)); + + SpanGenerator<char> nonBlankGenerator{nonBlankChars.span().takeOkay()}; + auto utf8NonBlankGenerator = [&]() { return nonBlankGenerator().map(toUtf8<uint16_t>); }; + ASSERT_FALSE(anyOf(utf8NonBlankGenerator, utf8IsBlank)); } TEST(Utf8Test, Utf8IsPrintable) { - constexpr size_t numPrintableChars = validAsciiSize - numControlChars; - auto printableChars = generateIndexArray<Array, char, numPrintableChars>([](int idx) { - return static_cast<char>(idx + printableCharOffset); - }); - - ASSERT_TRUE(allOf(printableChars, ext_lib_char_is_printable)); + constexpr size_t numPrintableChars = sizeof(PRINTABLE_CHARS) - 1; + Array<char, numPrintableChars + 1> printableCharLiteral{PRINTABLE_CHARS}; + Array<char, numPrintableChars> printableChars{}; + copyMem<numPrintableChars>(printableChars, printableCharLiteral); - auto printableSetResult{StaticSet<char, numPrintableChars>::makeFromArray(printableChars)}; - ASSERT_TRUE(printableSetResult.isOkay()); - auto printableSet{std::move(printableSetResult.getOkay())}; + ASSERT_TRUE(allOf(printableChars.span(), charIsPrintable)); constexpr size_t numNonPrintableChars = validAsciiSize - numPrintableChars; - FixedVector<char, numNonPrintableChars> nonPrintableChars{}; - for (char idx = 0; idx < INT8_MAX; ++idx) { - if (!printableSet.isKey(idx)) { - ASSERT_TRUE(nonPrintableChars.pushBack(idx)); + Vector<char> nonPrintableChars{}; + ASSERT_EQ(nonPrintableChars.reserve(numNonPrintableChars), FudStatus::Success); + for (uint8_t idx = 0; idx < INT8_MAX + 1; ++idx) { + if (!charIsPrintable(static_cast<char>(idx))) { + ASSERT_EQ(nonPrintableChars.pushBack(static_cast<char>(idx)), FudStatus::Success); } } - ASSERT_FALSE(anyOf(nonPrintableChars, ext_lib_char_is_printable)); - - auto invalidAsciiChars = generateInvalidAsciiChars(); - ASSERT_FALSE(anyOf(invalidAsciiChars, ext_lib_char_is_printable)); - - ASSERT_TRUE(allOf( - map(printableChars, [](auto input) { return ExtUtf8Char4{static_cast<utf8>(input)}; }), - ext_lib_char4_is_printable)); - ASSERT_FALSE(anyOf( - map(nonPrintableChars, [](auto input) { return ExtUtf8Char4{static_cast<utf8>(input)}; }), - ext_lib_char4_is_printable)); - ASSERT_FALSE(anyOf( - map(invalidAsciiChars, [](auto input) { return ExtUtf8Char4{static_cast<utf8>(input)}; }), - ext_lib_char4_is_printable)); - - ASSERT_TRUE(allOf( - map(printableChars, [](auto input) { return ExtUtf8Char4{static_cast<utf8>(input)}; }), - ext_lib_char4_is_printable)); - ASSERT_FALSE(anyOf( - map(nonPrintableChars, [](auto input) { return ExtUtf8Char4{static_cast<utf8>(input)}; }), - ext_lib_char4_is_printable)); - ASSERT_FALSE(anyOf( - map(invalidAsciiChars, [](auto input) { return ExtUtf8Char4{static_cast<utf8>(input)}; }), - ext_lib_char4_is_printable)); - - Utf8 utf8{invalidAscii}; - bool isAscii = false; - ASSERT_EQ(ext_lib_utf8_is_printable(nullptr, nullptr), ExtNullPointer); - ASSERT_EQ(ext_lib_utf8_is_printable(&utf8, nullptr), ExtNullPointer); - ASSERT_EQ(ext_lib_utf8_is_printable(nullptr, &isAscii), ExtNullPointer); - ASSERT_EQ(ext_lib_utf8_is_printable(&utf8, &isAscii), ExtInvalidInput); - ASSERT_FALSE(isAscii); - - ASSERT_TRUE(allOf( - map(printableChars, - [](char letter) { return FudUtf8::makeUtf8(ExtUtf8Char4{static_cast<utf8>(letter)}); }), - [](auto& utf8Letter) { - bool predicate = false; - auto isPredicateStatus = ext_lib_utf8_is_printable(&utf8Letter, &predicate); - return isPredicateStatus == ExtSuccess && predicate; - })); - ASSERT_FALSE(anyOf( - map(nonPrintableChars, - [](char letter) { return FudUtf8::makeUtf8(ExtUtf8Char4{static_cast<utf8>(letter)}); }), - [](auto& utf8Letter) { - bool predicate = false; - auto isPredicateStatus = ext_lib_utf8_is_printable(&utf8Letter, &predicate); - return isPredicateStatus != ExtSuccess || predicate; - })); - ASSERT_FALSE(allOf( - map(nonPrintableChars, - [](char letter) { return FudUtf8::makeUtf8(ExtUtf8Char4{static_cast<utf8>(letter)}); }), - [](auto& utf8Letter) { - bool predicate = false; - auto isPredicateStatus = ext_lib_utf8_is_printable(&utf8Letter, &predicate); - return isPredicateStatus == ExtInvalidInput && !predicate; - })); + ASSERT_FALSE(anyOf(nonPrintableChars.span().takeOkay(), charIsPrintable)); + ASSERT_TRUE(allOf(nonPrintableChars.span().takeOkay(), charIsAscii)); + ASSERT_EQ(nonPrintableChars.size() + printableChars.size(), INT8_MAX + 1); + + auto invalidAsciiChars = invalidAsciiGenerator(); + ASSERT_FALSE(anyOf(invalidAsciiChars, charIsPrintable)); + + ASSERT_FALSE(utf8IsPrintable(FudUtf8{Ascii{invalidAscii}})); + + SpanGenerator<char, printableChars.size()> printableGenerator{printableChars.span()}; + auto utf8PrintableGenerator = [&]() { return printableGenerator().map(toUtf8<uint16_t>); }; + ASSERT_TRUE(allOf(utf8PrintableGenerator, utf8IsPrintable)); + + SpanGenerator<char> nonPrintableGenerator{nonPrintableChars.span().takeOkay()}; + auto utf8NonPrintableGenerator = [&]() { return nonPrintableGenerator().map(toUtf8<uint16_t>); }; + ASSERT_FALSE(anyOf(utf8NonPrintableGenerator, utf8IsPrintable)); } TEST(Utf8Test, Utf8IsPunctuation) @@ -1087,77 +611,32 @@ TEST(Utf8Test, Utf8IsPunctuation) Array<char, numPunctuationChars> punctuationChars{}; copyMem<numPunctuationChars>(punctuationChars, punctuationCharLiteral); - ASSERT_TRUE(allOf(punctuationChars, ext_lib_char_is_punctuation)); - - auto punctuationSetResult{StaticSet<char, numPunctuationChars>::makeFromArray(punctuationChars)}; - ASSERT_TRUE(punctuationSetResult.isOkay()); - auto punctuationSet{std::move(punctuationSetResult.getOkay())}; + ASSERT_TRUE(allOf(punctuationChars.span(), charIsPunctuation)); constexpr size_t numNonPunctuationChars = validAsciiSize - numPunctuationChars; - FixedVector<char, numNonPunctuationChars> nonPunctuationChars{}; - for (char idx = 0; idx < INT8_MAX; ++idx) { - if (!punctuationSet.isKey(idx)) { - ASSERT_TRUE(nonPunctuationChars.pushBack(idx)); + Vector<char> nonPunctuationChars{}; + ASSERT_EQ(nonPunctuationChars.reserve(numNonPunctuationChars), FudStatus::Success); + for (uint8_t idx = 0; idx < INT8_MAX + 1; ++idx) { + if (!charIsPunctuation(static_cast<char>(idx))) { + ASSERT_EQ(nonPunctuationChars.pushBack(static_cast<char>(idx)), FudStatus::Success); } } - ASSERT_FALSE(anyOf(nonPunctuationChars, ext_lib_char_is_punctuation)); - - auto invalidAsciiChars = generateInvalidAsciiChars(); - ASSERT_FALSE(anyOf(invalidAsciiChars, ext_lib_char_is_punctuation)); - - ASSERT_TRUE(allOf( - map(punctuationChars, [](auto input) { return ExtUtf8Char4{static_cast<utf8>(input)}; }), - ext_lib_char4_is_punctuation)); - ASSERT_FALSE(anyOf( - map(nonPunctuationChars, [](auto input) { return ExtUtf8Char4{static_cast<utf8>(input)}; }), - ext_lib_char4_is_punctuation)); - ASSERT_FALSE(anyOf( - map(invalidAsciiChars, [](auto input) { return ExtUtf8Char4{static_cast<utf8>(input)}; }), - ext_lib_char4_is_punctuation)); - - ASSERT_TRUE(allOf( - map(punctuationChars, [](auto input) { return ExtUtf8Char4{static_cast<utf8>(input)}; }), - ext_lib_char4_is_punctuation)); - ASSERT_FALSE(anyOf( - map(nonPunctuationChars, [](auto input) { return ExtUtf8Char4{static_cast<utf8>(input)}; }), - ext_lib_char4_is_punctuation)); - ASSERT_FALSE(anyOf( - map(invalidAsciiChars, [](auto input) { return ExtUtf8Char4{static_cast<utf8>(input)}; }), - ext_lib_char4_is_punctuation)); - - Utf8 utf8{invalidAscii}; - bool isAscii = false; - ASSERT_EQ(ext_lib_utf8_is_punctuation(nullptr, nullptr), ExtNullPointer); - ASSERT_EQ(ext_lib_utf8_is_punctuation(&utf8, nullptr), ExtNullPointer); - ASSERT_EQ(ext_lib_utf8_is_punctuation(nullptr, &isAscii), ExtNullPointer); - ASSERT_EQ(ext_lib_utf8_is_punctuation(&utf8, &isAscii), ExtInvalidInput); - ASSERT_FALSE(isAscii); - - ASSERT_TRUE(allOf( - map(punctuationChars, - [](char letter) { return FudUtf8::makeUtf8(ExtUtf8Char4{static_cast<utf8>(letter)}); }), - [](auto& utf8Letter) { - bool predicate = false; - auto isPredicateStatus = ext_lib_utf8_is_punctuation(&utf8Letter, &predicate); - return isPredicateStatus == ExtSuccess && predicate; - })); - ASSERT_FALSE(anyOf( - map(nonPunctuationChars, - [](char letter) { return FudUtf8::makeUtf8(ExtUtf8Char4{static_cast<utf8>(letter)}); }), - [](auto& utf8Letter) { - bool predicate = false; - auto isPredicateStatus = ext_lib_utf8_is_punctuation(&utf8Letter, &predicate); - return isPredicateStatus != ExtSuccess || predicate; - })); - ASSERT_FALSE(allOf( - map(nonPunctuationChars, - [](char letter) { return FudUtf8::makeUtf8(ExtUtf8Char4{static_cast<utf8>(letter)}); }), - [](auto& utf8Letter) { - bool predicate = false; - auto isPredicateStatus = ext_lib_utf8_is_punctuation(&utf8Letter, &predicate); - return isPredicateStatus == ExtInvalidInput && !predicate; - })); + ASSERT_FALSE(anyOf(nonPunctuationChars.span().takeOkay(), charIsPunctuation)); + ASSERT_TRUE(allOf(nonPunctuationChars.span().takeOkay(), charIsAscii)); + ASSERT_EQ(nonPunctuationChars.size() + punctuationChars.size(), INT8_MAX + 1); + + auto invalidAsciiChars = invalidAsciiGenerator(); + ASSERT_FALSE(anyOf(invalidAsciiChars, charIsPunctuation)); + + ASSERT_FALSE(utf8IsPunctuation(FudUtf8{Ascii{invalidAscii}})); + + SpanGenerator<char, punctuationChars.size()> punctuationGenerator{punctuationChars.span()}; + auto utf8PunctuationGenerator = [&]() { return punctuationGenerator().map(toUtf8<uint16_t>); }; + ASSERT_TRUE(allOf(utf8PunctuationGenerator, utf8IsPunctuation)); + + SpanGenerator<char> nonPunctuationGenerator{nonPunctuationChars.span().takeOkay()}; + auto utf8NonPunctuationGenerator = [&]() { return nonPunctuationGenerator().map(toUtf8<uint16_t>); }; + ASSERT_FALSE(anyOf(utf8NonPunctuationGenerator, utf8IsPunctuation)); } -#endif } // namespace fud diff --git a/test/test_vector.cpp b/test/test_vector.cpp new file mode 100644 index 0000000..b4fd83c --- /dev/null +++ b/test/test_vector.cpp @@ -0,0 +1,138 @@ +/* + * 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_vector.hpp" + +#include "gtest/gtest.h" + +namespace fud { + +TEST(VectorTest, TrivialVector) +{ + Vector<int> intVector{}; + ASSERT_EQ(intVector.size(), 0); + ASSERT_EQ(intVector.capacity(), 0); + ASSERT_TRUE(intVector.ref(0).isError()); + + ASSERT_EQ(intVector.resize(10), FudStatus::Success); + ASSERT_EQ(intVector.size(), 10); + ASSERT_EQ(intVector.capacity(), 10); + + ASSERT_TRUE(intVector.ref(0).isOkay()); + ASSERT_EQ(intVector.ref(0).getOkay(), 0); + intVector.get(0).takeOkay().get() = 10; + ASSERT_EQ(intVector.ref(0).getOkay(), 10); +} + +struct NonTrivial { + static thread_local int counter; + NonTrivial() + { + counter++; + } + explicit NonTrivial(int val) : value{val} + { + counter++; + } + NonTrivial(const NonTrivial&) = delete; + NonTrivial(NonTrivial&& rhs) : value{rhs.value}, destroyed{rhs.destroyed} + { + rhs.destroyed = true; + } + ~NonTrivial() + { + if (!destroyed) { + counter--; + destroyed = true; + } + } + NonTrivial& operator=(const NonTrivial& rhs) = delete; + NonTrivial& operator=(NonTrivial&& rhs) { + value = rhs.value; + destroyed = rhs.destroyed; + rhs.destroyed = true; + return *this; + } + int value{0}; + bool destroyed{false}; +}; + +int thread_local NonTrivial::counter = 0; + +TEST(VectorTest, NonTrivialVector) +{ + auto& counter = NonTrivial::counter; + counter = 0; + Vector<NonTrivial> nonTrivialVector{}; + ASSERT_EQ(nonTrivialVector.size(), 0); + ASSERT_EQ(nonTrivialVector.capacity(), 0); + ASSERT_TRUE(nonTrivialVector.ref(0).isError()); + ASSERT_EQ(counter, 0); + + ASSERT_EQ(nonTrivialVector.resize(10), FudStatus::Success); + ASSERT_EQ(nonTrivialVector.size(), 10); + ASSERT_EQ(nonTrivialVector.capacity(), 10); + ASSERT_EQ(counter, 10); + + ASSERT_TRUE(nonTrivialVector.ref(0).isOkay()); + ASSERT_EQ(nonTrivialVector.ref(0).getOkay().get().value, 0); + nonTrivialVector.get(0).takeOkay().get().value = 10; + ASSERT_EQ(nonTrivialVector.ref(0).getOkay().get().value, 10); + + ASSERT_EQ(nonTrivialVector.pushBack(NonTrivial{42}), FudStatus::Success); + ASSERT_EQ(nonTrivialVector.size(), 11); + ASSERT_GE(nonTrivialVector.capacity(), 11); + ASSERT_EQ(counter, 11); + auto capacity = nonTrivialVector.capacity(); + ASSERT_EQ(nonTrivialVector.reserve(SIZE_MAX / sizeof(NonTrivial)), FudStatus::AllocFailure); + ASSERT_EQ(nonTrivialVector.capacity(), capacity); + + { + auto popResult{nonTrivialVector.popBack()}; + ASSERT_TRUE(popResult.isOkay()); + auto value{popResult.takeOkay()}; + ASSERT_EQ(value.value, 42); + ASSERT_EQ(counter, 11); + } + ASSERT_EQ(counter, 10); + ASSERT_EQ(nonTrivialVector.eraseBack(), FudStatus::Success); + ASSERT_EQ(counter, 9); + + int val = 1; + for (auto& element: nonTrivialVector) { + element.value = val; + val++; + } + ASSERT_EQ(nonTrivialVector.insert(3, NonTrivial{13}), FudStatus::Success); + for (size_t idx = 0; idx < 3; ++idx) { + ASSERT_EQ(nonTrivialVector[idx].value, idx + 1); + } + ASSERT_EQ(counter, 10); + ASSERT_EQ(nonTrivialVector[3].value, 13); + for (size_t idx = 4; idx < nonTrivialVector.size(); ++idx) { + ASSERT_EQ(nonTrivialVector[idx].value, idx); + } + ASSERT_EQ(counter, nonTrivialVector.size()); + + ASSERT_EQ(nonTrivialVector.erase(3), FudStatus::Success); + for (size_t idx = 0; idx < nonTrivialVector.size(); ++idx) { + EXPECT_EQ(nonTrivialVector[idx].value, idx + 1); + } + ASSERT_EQ(counter, nonTrivialVector.size()); +} + +} // namespace fud |