summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDominick Allen <djallen@librehumanitas.org>2024-10-20 10:48:19 -0500
committerDominick Allen <djallen@librehumanitas.org>2024-10-20 10:48:19 -0500
commit6a27a2a4032e88fa9154ef0f0741edc584f7a701 (patch)
tree92ca58cbcdd2c1d11b7d69deb0d4925d0f979a3f
parente94db4695e236b42ae1be44b2605075161d5144f (diff)
Lots of work.
-rw-r--r--.clang-format1
-rw-r--r--CMakeLists.txt12
-rw-r--r--include/fud_algorithm.hpp162
-rw-r--r--include/fud_array.hpp4
-rw-r--r--include/fud_c_string.hpp55
-rw-r--r--include/fud_format.hpp362
-rw-r--r--include/fud_result.hpp35
-rw-r--r--include/fud_span.hpp75
-rw-r--r--include/fud_status.hpp3
-rw-r--r--include/fud_string.hpp1
-rw-r--r--include/fud_string_view.hpp14
-rw-r--r--include/fud_utf8.hpp130
-rw-r--r--include/fud_vector.hpp64
-rw-r--r--source/fud_assert.cpp8
-rw-r--r--source/fud_format.cpp14
-rw-r--r--source/fud_string.cpp25
-rw-r--r--source/fud_string_view.cpp38
-rw-r--r--source/fud_utf8.cpp174
-rw-r--r--source/fud_utf8_iterator.cpp4
-rw-r--r--test/CMakeLists.txt12
-rw-r--r--test/test_common.hpp11
-rw-r--r--test/test_format.cpp12
-rw-r--r--test/test_utf8.cpp1163
23 files changed, 1914 insertions, 465 deletions
diff --git a/.clang-format b/.clang-format
index 87aff92..50883a6 100644
--- a/.clang-format
+++ b/.clang-format
@@ -30,7 +30,6 @@ AlignConsecutiveMacros:
PadOperators: false
AlignEscapedNewlines: Left
AlignOperands: false
-AlignTrailingComments: Never
AllowAllArgumentsOnNextLine: false
AllowAllParametersOfDeclarationOnNextLine: false
AllowShortBlocksOnASingleLine: Never
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 7358151..f0e9aff 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -19,14 +19,15 @@ add_library(fud SHARED
source/libfud.cpp
source/fud_allocator.cpp
source/fud_assert.cpp
- source/fud_memory.cpp
+ source/fud_format.cpp
+ source/fud_directory.cpp
source/fud_c_file.cpp
+ source/fud_memory.cpp
+ source/fud_sqlite.cpp
source/fud_string_view.cpp
source/fud_string.cpp
source/fud_utf8.cpp
source/fud_utf8_iterator.cpp
- source/fud_sqlite.cpp
- source/fud_directory.cpp
)
include(cmake/warnings.cmake)
@@ -39,7 +40,7 @@ target_link_libraries(fud ${SQLite3_LIBRARIES})
set_target_properties(
fud PROPERTIES
- CXX_STANDARD 20
+ CXX_STANDARD 23
C_STANDARD 23
CXX_EXTENSIONS OFF
C_EXTENSIONS OFF
@@ -89,9 +90,11 @@ install(FILES ${CMAKE_CURRENT_BINARY_DIR}/FudConfig.cmake
set(FUD_HEADERS
"include/libfud.hpp"
"include/fud_allocator.hpp"
+ "include/fud_algorithm.hpp"
"include/fud_array.hpp"
"include/fud_assert.hpp"
"include/fud_c_file.hpp"
+ "include/fud_c_string.hpp"
"include/fud_directory.hpp"
"include/fud_fud_type_traits.hpp"
"include/fud_memory.hpp"
@@ -105,6 +108,7 @@ set(FUD_HEADERS
"include/fud_unique_array.hpp"
"include/fud_utf8.hpp"
"include/fud_utf8_iterator.hpp"
+ "include/fud_vector.hpp"
)
set_target_properties(fud PROPERTIES PUBLIC_HEADER "${FUD_HEADERS}")
diff --git a/include/fud_algorithm.hpp b/include/fud_algorithm.hpp
new file mode 100644
index 0000000..e3d5d3b
--- /dev/null
+++ b/include/fud_algorithm.hpp
@@ -0,0 +1,162 @@
+/*
+ * 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_ALGORITHM_HPP
+#define FUD_ALGORITHM_HPP
+
+#include "fud_span.hpp"
+
+#include <concepts>
+#include <limits>
+#include <optional>
+#include <type_traits>
+
+namespace fud {
+
+template <std::integral T>
+class Iota {
+ public:
+ constexpr Iota() noexcept : m_value{}, m_increment{static_cast<T>(1)}, m_limit{std::numeric_limits<T>::max()}
+ {
+ }
+
+ constexpr Iota(T value) noexcept :
+ m_value{value}, m_increment{static_cast<T>(1)}, m_limit{std::numeric_limits<T>::max()}
+ {
+ }
+
+ constexpr Iota(T value, T increment) noexcept :
+ m_value{value}, m_increment{increment}, m_limit{std::numeric_limits<T>::max()}
+ {
+ }
+
+ constexpr Iota(T value, T increment, T limit) noexcept : m_value{value}, m_increment{increment}, m_limit{limit}
+ {
+ }
+
+ constexpr std::optional<T> operator()() noexcept
+ {
+ auto value = m_value;
+ if (m_increment > 0) {
+ if (m_limit - m_increment < m_value) {
+ return std::nullopt;
+ }
+ } else {
+ if (m_limit + m_increment + 1 >= m_value) {
+ return std::nullopt;
+ }
+ }
+ m_value += m_increment;
+ return value;
+ }
+
+ void set(T value) {
+ m_value = value;
+ }
+
+ private:
+ T m_value;
+ const T m_increment;
+ const T m_limit;
+};
+
+template <typename T, size_t Size, typename Func>
+Span<T, Size> forEach(Span<T, Size> input, Func&& mapFunc)
+{
+ for (auto& element : input) {
+ element = std::forward<Func>(mapFunc)(element);
+ }
+
+ return input;
+}
+
+template <typename T, typename U, size_t Size, typename Func>
+Span<T, Size> mapTo(Span<T, Size> input, Span<U, Size> output, Func&& mapFunc)
+{
+ for (auto idx = 0; idx < input.size(); ++idx) {
+ output[idx] = std::forward<Func>(mapFunc)(input[idx]);
+ }
+
+ return input;
+}
+
+template <typename T, size_t Size, typename Func, typename Builder, typename Output>
+auto map(Span<T, Size> input, Func&& mapFunc, Builder&& builder) -> 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;
+}
+
+template <typename Generator, typename Builder>
+auto generate(Builder&& builder, Generator&& generator) -> decltype(std::forward<Builder>(builder)())
+{
+ using Output = decltype(std::forward<Builder>(builder)());
+ Output output{std::forward<Builder>(builder)()};
+ for (auto idx = 0; idx < output.size(); ++idx) {
+ output[idx] = std::forward<Generator>(generator)();
+ }
+
+ return output;
+}
+
+template <typename T, size_t Size, typename Func>
+bool allOf(Span<T, Size> input, Func&& predicate)
+{
+ bool result = input.size() > 0;
+ for (size_t idx = 0; result && idx < input.size(); ++idx) {
+ result = result && std::forward<Func>(predicate)(input[idx]);
+ }
+ return result;
+}
+
+template <typename Generator, typename Func>
+bool allOf(Generator&& generator, Func&& predicate)
+{
+ bool result = true;
+ while (auto val = std::forward<Generator>(generator)()) {
+ result = result && std::forward<Func>(predicate)(*val);
+ }
+ return result;
+}
+
+template <typename T, size_t Size, typename Func>
+bool anyOf(Span<T, Size> input, Func&& predicate)
+{
+ bool result = !(input.size() > 0);
+ for (size_t idx = 0; result && idx < input.size(); ++idx) {
+ result = result || std::forward<Func>(predicate)(input[idx]);
+ }
+ return result;
+}
+
+template <typename Generator, typename Func>
+bool anyOf(Generator&& generator, Func&& predicate)
+{
+ bool result = false;
+ while (auto val = std::forward<Generator>(generator)()) {
+ result = result || std::forward<Func>(predicate)(*val);
+ }
+ return result;
+}
+
+} // namespace fud
+
+#endif
diff --git a/include/fud_array.hpp b/include/fud_array.hpp
index 4e2c702..807621a 100644
--- a/include/fud_array.hpp
+++ b/include/fud_array.hpp
@@ -18,9 +18,9 @@
#ifndef FUD_ARRAY_HPP
#define FUD_ARRAY_HPP
-#include "fud_memory.hpp"
+#include <cstddef>
-#include <cstdlib>
+#include "fud_memory.hpp"
namespace fud {
diff --git a/include/fud_c_string.hpp b/include/fud_c_string.hpp
new file mode 100644
index 0000000..44e0dc8
--- /dev/null
+++ b/include/fud_c_string.hpp
@@ -0,0 +1,55 @@
+/*
+ * libfud
+ * Copyright 2024 Dominick Allen
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FUD_C_STRING_HPP
+#define FUD_C_STRING_HPP
+
+#include <climits>
+#include <cstddef>
+#include <sys/types.h>
+
+
+namespace fud {
+
+constexpr ssize_t cStringLength(const char* str, size_t maxLength)
+{
+ if (str == nullptr || maxLength > (SSIZE_MAX - 1)) {
+ return -1;
+ }
+
+ ssize_t size = 0;
+
+ while (str[size] != 0 && static_cast<size_t>(size) < maxLength) {
+ size++;
+ }
+
+ if (str[size] != 0 && static_cast<size_t>(size) == maxLength) {
+ return static_cast<ssize_t>(maxLength) + 1;
+ }
+
+ return size;
+}
+
+constexpr ssize_t cStringLength(const char* str)
+{
+ constexpr auto maxLength = SSIZE_MAX - 1;
+ return cStringLength(str, maxLength);
+}
+
+} // namespace fud
+
+#endif
diff --git a/include/fud_format.hpp b/include/fud_format.hpp
index 8985faf..ea32bd8 100644
--- a/include/fud_format.hpp
+++ b/include/fud_format.hpp
@@ -18,275 +18,179 @@
#ifndef FUD_FORMAT_HPP
#define FUD_FORMAT_HPP
-#include "fud_assert.hpp"
+// #include "fud_assert.hpp"
#include "fud_result.hpp"
-#include "fud_span.hpp"
#include "fud_status.hpp"
#include "fud_string_view.hpp"
+#include "fud_utf8.hpp"
-#include <format> // for std::format_string
#include <cstdint>
#include <optional>
+#include <variant>
namespace fud {
-template <size_t Size>
-using CharSpan = Span<char, Size>;
-
-template <typename... Args>
-using FormatLiteral = std::format_string<Args...>;
+struct FormatAlign
+{
+ enum class Value : uint8_t
+ {
+ Left,
+ Right,
+ Center
+ };
+
+ constexpr static std::optional<FormatAlign> from(utf8 letter)
+ {
+ FormatAlign formatAlign;
+ switch (letter) {
+ case '<':
+ formatAlign.value = Value::Left;
+ break;
+ case '>':
+ formatAlign.value = Value::Right;
+ break;
+ case '^':
+ formatAlign.value = Value::Center;
+ break;
+ default:
+ return std::nullopt;
+ }
-template <typename... Args, size_t Size>
-Result<size_t, FudStatus> format(CharSpan<Size> buffer, FormatLiteral<Args...> formatLiteral, Args&&... args);
+ return formatAlign;
+ }
-enum class FormatAlign : uint8_t {
- Left,
- Right,
- Center
+ Value value;
};
struct FormatFill {
FormatAlign align;
- char fill;
+ utf8 fill;
+
+ constexpr static Result<std::optional<FormatFill>, FudStatus> parse(StringView formatView) {
+ // "{:A<X}"
+ using RetType = Result<std::optional<FormatFill>, FudStatus>;
+ if (formatView.length() < 3) {
+ return RetType::okay(std::nullopt);
+ }
+
+ const auto* data = formatView.data();
+ if (data[0] != 'A') {
+ return RetType::okay(std::nullopt);
+ }
+
+ auto align = FormatAlign::from(data[1]);
+ if (!align.has_value()) {
+ return FudStatus::FormatInvalid;
+ }
+
+ auto fill = data[2];
+ if (!Ascii::valid(fill)) {
+ return FudStatus::Utf8Invalid;
+ }
+
+ return RetType::okay(FormatFill{*align, fill});
+ }
};
-enum class FormatSign : uint8_t {
+enum class FormatSign : uint8_t
+{
Plus,
Minus,
Space
};
-struct FormatSpec {
- std::optional<FormatFill> fill;
- std::optional<FormatSign> formatSign;
- uint32_t minWidth;
+enum class FormatStringType : uint8_t
+{
+ String,
+ Escaped,
};
-namespace detail {
-
-template <typename Arg, typename... Args, size_t Size>
-Result<size_t, FudStatus> formatHelper(
- CharSpan<Size> buffer,
- size_t formattedSize,
- StringView formatView,
- Arg&& arg,
- Args&&... args);
-
-template <typename Arg, typename... Args, size_t Size>
-Result<size_t, FudStatus> formatHelper(
- CharSpan<Size> buffer,
- size_t formattedSize,
- StringView formatView,
- Arg&& arg);
-
-template <size_t Size>
-Result<size_t, FudStatus> formatHelper(
- CharSpan<Size> buffer,
- size_t formattedSize,
- StringView formatView);
-
-} // namespace detail
-
-template <typename... Args, size_t Size>
-Result<size_t, FudStatus> format(CharSpan<Size> buffer, FormatLiteral<Args...> formatLiteral, Args&&... args)
+enum class FormatIntegerType : uint8_t
{
- static_assert(Size > 0);
-
- if (buffer.data() == nullptr) {
- return FudStatus::NullPointer;
- }
-
- StringView formatView{formatLiteral.get()};
-
- if (formatView.length() == 0 || formatView.data()[0] == '\0') {
- return 0U;
- }
-
- size_t argCount = sizeof...(args);
- static_cast<void>(argCount);
-
- size_t formattedSize = 0;
+ BinaryLower,
+ BinaryUpper,
+ Character,
+ Decimal,
+ Octal,
+ HexLower,
+ HexUpper,
+};
- return detail::formatHelper(buffer, formattedSize, formatView, std::forward<Args>(args)...);
-}
+enum class FormatCharacterType : uint8_t
+{
+ BinaryLower,
+ BinaryUpper,
+ Character,
+ Decimal,
+ Octal,
+ HexLower,
+ HexUpper,
+};
-namespace detail {
+enum class FormatBoolType : uint8_t
+{
+ BinaryLower,
+ BinaryUpper,
+ Character,
+ Decimal,
+ Octal,
+ HexLower,
+ HexUpper,
+};
-#define FUDETAIL_ADVANCE_FORMAT(FORMAT_VIEW, ADVANCE_BY) \
- fudAssert(ADVANCE_BY <= FORMAT_VIEW.m_length); \
- FORMAT_VIEW.m_length -= ADVANCE_BY; \
- FORMAT_VIEW.m_data += ADVANCE_BY; \
+enum class FormatFloatingType : uint8_t
+{
+ FloatHexLower,
+ FloatHexUpper,
+ ScientificLower,
+ ScientificUpper,
+ Fixed,
+ GeneralLower,
+ GeneralUpper
+};
-constexpr bool findBracket(size_t& copyLength, StringView formatView)
+enum class FormatPointerType : uint8_t
{
- while (copyLength < formatView.m_length) {
- if (formatView.m_data[copyLength] == '{') {
- return true;
- }
- copyLength++;
- }
+ HexLower,
+ HexUpper
+};
- return false;
-}
+using FormatType = std::variant< // break
+ std::monostate,
+ FormatStringType,
+ FormatIntegerType,
+ FormatCharacterType,
+ FormatBoolType,
+ FormatFloatingType,
+ FormatPointerType>;
-template <size_t Size>
-size_t copyRemaining(
- CharSpan<Size> buffer,
- size_t formattedSize,
- StringView& formatView,
- size_t copyLength)
-{
- fudAssert(copyLength <= formatView.length());
- if (copyLength + formattedSize > Size) {
- copyLength = Size - formattedSize;
- }
- auto copyResult = copyMem(
- buffer.data() + formattedSize,
- Size - formattedSize,
- formatView.m_data,
- copyLength);
- fudAssert(copyResult == FudStatus::Success);
- FUDETAIL_ADVANCE_FORMAT(formatView, copyLength);
- return formattedSize + copyLength;
-}
-
-template <typename Arg, size_t Size>
-Result<size_t, FudStatus> handleSpec(
- CharSpan<Size> buffer,
- size_t formattedSize,
- StringView& formatView,
- Arg&& arg,
- bool& consumed)
-{
- fudAssert(formattedSize < Size);
- fudAssert(formatView.length() > 1);
-
- if (formatView.m_data[1] == '{') {
- consumed = false;
- buffer[formattedSize] = '{';
- FUDETAIL_ADVANCE_FORMAT(formatView, 2);
- return formattedSize + 1;
- }
+struct FormatSpec;
+using FormatSpecResult = Result<FormatSpec, FudStatus>;
- static_cast<void>(arg);
- buffer[formattedSize] = 'X';
- formattedSize += 1;
- size_t index = 0;
- for (; index < formatView.m_length; ++index) {
- if (formatView.m_data[index] == '}') {
- break;
- }
- }
- FUDETAIL_ADVANCE_FORMAT(formatView, index + 1);
- return formattedSize;
-}
-
-template <typename Arg, typename... Args, size_t Size>
-Result<size_t, FudStatus> formatHelper(
- CharSpan<Size> buffer,
- size_t formattedSize,
- StringView formatView,
- Arg&& arg,
- Args&&... args)
-{
- while (formattedSize < Size) {
- size_t copyLength = 0;
- auto found = findBracket(copyLength, formatView);
- formattedSize = copyRemaining(buffer, formattedSize, formatView, copyLength);
- fudAssert(formattedSize <= Size);
- if (!found || formattedSize == Size) {
- return formattedSize;
- }
+struct FormatSpec {
+ size_t width;
+ size_t precision;
+ FormatFill fill;
+ FormatSign formatSign;
- bool consumed = false;
- auto specResult = handleSpec(buffer, formattedSize, formatView, std::forward<Arg>(arg), consumed);
- formattedSize = M_TakeOrReturn(specResult);
- fudAssert(formattedSize <= Size);
- if (formattedSize == Size) {
- return formattedSize;
- }
+ FormatType formatType;
- if (consumed) {
- return formatHelper(buffer, formattedSize, formatView, std::forward<Args>(args)...);
- }
- }
+ bool hasWidth;
+ bool takesWidth;
- return formattedSize;
-}
+ bool hasPrecision;
+ bool takesPrecision;
-template <typename Arg, typename... Args, size_t Size>
-Result<size_t, FudStatus> formatHelper(
- CharSpan<Size> buffer,
- size_t formattedSize,
- StringView formatView,
- Arg&& arg)
-{
- while (formattedSize < Size) {
- size_t copyLength = 0;
- auto found = findBracket(copyLength, formatView);
- formattedSize = copyRemaining(buffer, formattedSize, formatView, copyLength);
- fudAssert(formattedSize <= Size);
- if (!found || formattedSize == Size) {
- return formattedSize;
- }
+ bool hasFill;
- bool consumed = false;
- auto specResult = handleSpec(buffer, formattedSize, formatView, std::forward<Arg>(arg), consumed);
- formattedSize = M_TakeOrReturn(specResult);
- if (consumed) {
- return formatHelper(buffer, formattedSize, formatView);
- }
- }
- return formattedSize;
-}
-
-template <size_t Size>
-Result<size_t, FudStatus> formatHelper(
- CharSpan<Size> buffer,
- size_t formattedSize,
- StringView formatView)
-{
- size_t index = 0;
- while (formattedSize < Size && formatView.m_length > 0) {
- while (index < formatView.m_length && formattedSize + index < Size) {
- if (formatView.m_data[index] == '{') {
- break;
- }
- index++;
- }
- bool isBracket{false};
- if (index + 1 < formatView.m_length && formattedSize + index + 1 < Size) {
- if (formatView.m_data[index] == '{') {
- isBracket = true;
- index++;
- }
- }
- auto copyResult = copyMem(
- buffer.data() + formattedSize,
- Size - formattedSize,
- formatView.m_data,
- index);
- formattedSize += index;
- formatView.m_length -= index;
- formatView.m_data += index;
- if (isBracket) {
- index = 0;
- if (formatView.m_length > 0) {
- formatView.m_length--;
- formatView.m_data++;
- }
- if (formattedSize < Size) {
- buffer.data()[formattedSize] = 'X';
- formattedSize++;
- }
- }
- }
- return formattedSize;
-}
+ bool hasFormatSign;
+
+ bool alternateForm;
-#undef FUDETAIL_ADVANCE_FORMAT
+ bool leadingZero;
-} // namespace detail
+ static Result<FormatSpec, FudStatus> make(StringView& formatView, size_t specIndex);
+};
} // namespace fud
diff --git a/include/fud_result.hpp b/include/fud_result.hpp
index 4bfb819..877c49c 100644
--- a/include/fud_result.hpp
+++ b/include/fud_result.hpp
@@ -19,6 +19,7 @@
#define FUD_RESULT_HPP
#include <variant>
+#include <utility>
namespace fud {
@@ -28,62 +29,62 @@ class [[nodiscard]] Result {
public:
using ResultType = Result<T, E>;
- Result(const T& value) : m_value{value}
+ constexpr Result(const T& value) : m_value{value}
{
}
- Result(const E& value) : m_value{value}
+ constexpr Result(const E& value) : m_value{value}
{
}
- Result(T&& value) : m_value{std::move(value)}
+ constexpr Result(T&& value) : m_value{std::move(value)}
{
}
- Result(E&& value) : m_value{std::move(value)}
+ constexpr Result(E&& value) : m_value{std::move(value)}
{
}
- static ResultType okay(const T& okay)
+ static constexpr ResultType okay(const T& okay)
{
return ResultType{okay};
}
- static ResultType okay(T&& okay)
+ static constexpr ResultType okay(T&& okay)
{
return ResultType{std::move(okay)};
}
- static ResultType error(const E& error)
+ static constexpr ResultType error(const E& error)
{
return ResultType{error};
}
- static ResultType error(E&& error)
+ static constexpr ResultType error(E&& error)
{
return ResultType{std::move(error)};
}
template <typename F>
- static ResultType okay(const Result<T, F>& okayRes)
+ static constexpr ResultType okay(const Result<T, F>& okayRes)
{
return ResultType{okayRes.getOkay()};
}
template <typename F>
- static ResultType okay(Result<T, F>&& okayRes)
+ static constexpr ResultType okay(Result<T, F>&& okayRes)
{
return ResultType{okayRes.takeOkay()};
}
template <typename U>
- static ResultType error(const Result<U, E>& errorRes)
+ static constexpr ResultType error(const Result<U, E>& errorRes)
{
return ResultType{errorRes.getError()};
}
template <typename U>
- static ResultType error(Result<U, E>&& errorRes)
+ static constexpr ResultType error(Result<U, E>&& errorRes)
{
return ResultType{errorRes.takeError()};
}
@@ -98,28 +99,28 @@ class [[nodiscard]] Result {
return (m_value.index() == 1);
}
- [[nodiscard]] const T& getOkay() const&
+ [[nodiscard]] constexpr const T& getOkay() const&
{
return std::get<T>(m_value);
}
- [[nodiscard]] const E& getError() const&
+ [[nodiscard]] constexpr const E& getError() const&
{
return std::get<E>(m_value);
}
- [[nodiscard]] T&& takeOkay()
+ [[nodiscard]] constexpr T&& takeOkay()
{
return std::move(std::get<T>(m_value));
}
- [[nodiscard]] E&& takeError()
+ [[nodiscard]] constexpr E&& takeError()
{
return std::move(std::get<E>(m_value));
}
private:
- Result() : m_value()
+ constexpr Result() : m_value()
{
}
diff --git a/include/fud_span.hpp b/include/fud_span.hpp
index cc693f8..5b8497e 100644
--- a/include/fud_span.hpp
+++ b/include/fud_span.hpp
@@ -18,41 +18,71 @@
#ifndef FUD_SPAN_HPP
#define FUD_SPAN_HPP
-#include <cstdlib>
-
#include "fud_array.hpp"
+#include "fud_result.hpp"
+#include "fud_status.hpp"
+
+#include <cstddef>
+#include <cstdint>
namespace fud {
-template <typename T, size_t Size>
+template <typename T, size_t Size = SIZE_MAX>
struct Span {
static_assert(Size > 0);
using ValueType = T;
- static Span make(Array<T, Size>& array) {
- Span<T, Size> output{};
- output.m_data = array.data();
+ static Span make(Array<T, Size>& array)
+ {
+ Span<T, Size> output{array.data(), Size};
return output;
}
+ static Result<Span, FudStatus> make(Array<T, Size>& array, size_t size)
+ {
+ if (size > Size) {
+ return FudStatus::ArgumentInvalid;
+ }
+ return Span<T, Size>{array.data(), Size};
+ }
+
template <typename U>
- static Span make(const Array<U, Size>& array) {
+ static Span make(const Array<U, Size>& array)
+ {
static_assert(std::convertible_to<U, T>);
- Span<T, Size> output{};
- output.m_data = array.data();
- return output;
+ return Span<T, Size>{array.data(), Size};
}
template <typename U>
- static Span make(Array<U, Size>& array) {
+ static Result<Span, FudStatus> make(const Array<U, Size>& array, size_t size)
+ {
static_assert(std::convertible_to<U, T>);
- Span<T, Size> output{};
- output.m_data = array.data();
- return output;
+ if (size > Size) {
+ return FudStatus::ArgumentInvalid;
+ }
+ return Span<T, Size>{array.data(), Size};
+ }
+
+ template <typename U>
+ static Span make(Array<U, Size>& array)
+ {
+ static_assert(std::convertible_to<U, T>);
+ return Span<T, Size>{array.data(), array.size()};
+ }
+
+ template <typename U>
+ static Result<Span, FudStatus> make(Array<U, Size>& array, size_t size)
+ {
+ static_assert(std::convertible_to<U, T>);
+ if (size > Size) {
+ return FudStatus::ArgumentInvalid;
+ }
+ return Span<T, Size>{array.data(), array.size()};
}
template <size_t ArraySize>
- static Span makeCStringBuffer(Array<T, ArraySize>& array) {
+ static Span makeCStringBuffer(Array<T, ArraySize>& array)
+ {
static_assert(ArraySize > Size);
Span<T, Size> output{};
output.m_data = array.data();
@@ -60,10 +90,15 @@ struct Span {
}
T* m_data;
+ const size_t m_size;
[[nodiscard]] constexpr size_t size() const
{
- return Size;
+ if constexpr (Size < SIZE_MAX) {
+ return Size;
+ } else {
+ return m_size;
+ }
}
constexpr T& front()
@@ -78,12 +113,12 @@ struct Span {
constexpr T& back()
{
- return m_data[Size - 1];
+ return m_data[size() - 1];
}
constexpr const T& back() const
{
- return m_data[Size - 1];
+ return m_data[size() - 1];
}
constexpr T* data() noexcept
@@ -108,12 +143,12 @@ struct Span {
constexpr T* end() noexcept
{
- return m_data + Size;
+ return m_data + size();
}
constexpr const T* end() const noexcept
{
- return m_data + Size;
+ return m_data + size();
}
constexpr T& operator[](size_t index)
diff --git a/include/fud_status.hpp b/include/fud_status.hpp
index bda646b..91048ac 100644
--- a/include/fud_status.hpp
+++ b/include/fud_status.hpp
@@ -40,6 +40,7 @@ enum class [[nodiscard]] FudStatus
RangeError,
VariantInvalid,
BadArrayLength,
+ FormatInvalid,
NotImplemented,
NotSupported
};
@@ -83,6 +84,8 @@ constexpr const char* FudStatusToString(FudStatus status)
return "VariantInvalid";
case FudStatus::BadArrayLength:
return "BadArrayLength";
+ case FudStatus::FormatInvalid:
+ return "FormatInvalid";
case FudStatus::NotImplemented:
return "NotImplemented";
case FudStatus::NotSupported:
diff --git a/include/fud_string.hpp b/include/fud_string.hpp
index ba05450..60a328f 100644
--- a/include/fud_string.hpp
+++ b/include/fud_string.hpp
@@ -23,6 +23,7 @@
#include "fud_result.hpp"
#include "fud_status.hpp"
#include "fud_string_view.hpp"
+#include "fud_c_string.hpp"
#include "fud_utf8.hpp"
#include <climits>
diff --git a/include/fud_string_view.hpp b/include/fud_string_view.hpp
index 7b4925e..8a47ae5 100644
--- a/include/fud_string_view.hpp
+++ b/include/fud_string_view.hpp
@@ -71,6 +71,14 @@ struct StringView {
Result<size_t, FudStatus> trimWhitespace();
+ [[nodiscard]] bool advance();
+
+ void advanceUnsafe();
+
+ [[nodiscard]] bool advance(size_t size);
+
+ void advanceUnsafe(size_t size);
+
FudStatus toUint8(uint8_t& number, uint8_t specifiedRadix, size_t& strLen) const;
FudStatus toUint16(uint16_t& number, uint8_t specifiedRadix, size_t& strLen) const;
@@ -96,12 +104,6 @@ struct StringView {
const utf8* m_data{nullptr};
};
-FudStatus skipWhitespace(StringView& view, size_t& skipIndex);
-
-ssize_t cStringLength(const char* str);
-
-ssize_t cStringLength(const char* str, size_t maxLength);
-
} // namespace fud
#endif
diff --git a/include/fud_utf8.hpp b/include/fud_utf8.hpp
index 539e0f4..3b1a6b7 100644
--- a/include/fud_utf8.hpp
+++ b/include/fud_utf8.hpp
@@ -19,8 +19,8 @@
#define FUD_UTF8_HPP
#include "fud_array.hpp"
-#include "fud_status.hpp"
#include "fud_unique_array.hpp"
+#include "fud_c_string.hpp"
#include <cstdint>
#include <optional>
@@ -28,6 +28,8 @@
namespace fud {
+
+
using utf8 = unsigned char;
class String;
@@ -110,6 +112,18 @@ struct Utf82Byte {
constexpr Utf82Byte(utf8 first, utf8 second) noexcept : characters{{first, second}}
{
}
+
+ __attribute__((nonnull))
+ constexpr Utf82Byte(const char* letterStr) noexcept : characters{}
+ {
+ auto length = cStringLength(letterStr, 2);
+ if (length < 2) {
+ return;
+ }
+ characters[0] = static_cast<utf8>(letterStr[0]);
+ characters[1] = static_cast<utf8>(letterStr[1]);
+ }
+
Array<utf8, 2> characters;
static constexpr size_t size() noexcept
{
@@ -145,6 +159,18 @@ struct Utf83Byte {
{
}
+ __attribute__((nonnull))
+ constexpr Utf83Byte(const char* letterStr) noexcept : characters{}
+ {
+ auto length = cStringLength(letterStr, 3);
+ if (length < 3) {
+ return;
+ }
+ characters[0] = static_cast<utf8>(letterStr[0]);
+ characters[1] = static_cast<utf8>(letterStr[1]);
+ characters[2] = static_cast<utf8>(letterStr[2]);
+ }
+
Array<utf8, 3> characters;
static constexpr size_t size() noexcept
@@ -187,6 +213,19 @@ struct Utf84Byte {
{
}
+ __attribute__((nonnull))
+ constexpr Utf84Byte(const char* letterStr) noexcept : characters{}
+ {
+ auto length = cStringLength(letterStr, 4);
+ if (length < 4) {
+ return;
+ }
+ characters[0] = static_cast<utf8>(letterStr[0]);
+ characters[1] = static_cast<utf8>(letterStr[1]);
+ characters[2] = static_cast<utf8>(letterStr[2]);
+ characters[3] = static_cast<utf8>(letterStr[3]);
+ }
+
Array<utf8, 4> characters;
static constexpr size_t size() noexcept
@@ -250,11 +289,12 @@ struct FudUtf8 {
Utf8Variant m_variant{Utf8Variant{Ascii{}}};
static constexpr Ascii invalidAsciiCode{Ascii{0xFF}};
- static FudUtf8 fromString(const String& fudString, size_t index) noexcept;
- static FudUtf8 fromStringView(StringView view, size_t index) noexcept;
- // static FudUtf8 fromStringView(const StringView& view, size_t index) noexcept;
- static constexpr FudUtf8 makeUtf8(const Array<utf8, 4>& data)
+ static FudUtf8 from(const String& fudString, size_t index) noexcept;
+
+ static FudUtf8 from(StringView view, size_t index) noexcept;
+
+ static constexpr FudUtf8 make(const Array<utf8, 4>& data)
{
FudUtf8 unicode{};
if (Ascii::valid(data[0])) {
@@ -271,7 +311,12 @@ struct FudUtf8 {
return unicode;
}
- static constexpr FudUtf8 makeUtf8(const Ascii& utf8Char)
+ static constexpr FudUtf8 make(utf8 utf8Char)
+ {
+ return make(Ascii{utf8Char});
+ }
+
+ static constexpr FudUtf8 make(Ascii utf8Char)
{
FudUtf8 unicode{{Utf8Variant{Ascii{}}}};
if (utf8Char.valid()) {
@@ -282,6 +327,15 @@ struct FudUtf8 {
return unicode;
}
+ static constexpr FudUtf8 make(Utf8Variant utf8Variant) {
+ FudUtf8 unicode{};
+ unicode.m_variant = utf8Variant;
+ if (!std::visit([](auto arg) { return arg.valid(); }, utf8Variant)) {
+ unicode.m_variant = invalidAsciiCode;
+ }
+ return unicode;
+ }
+
static constexpr FudUtf8 invalidAscii()
{
FudUtf8 character{};
@@ -460,89 +514,93 @@ struct FudUtf8 {
};
/** \brief Checks if a character is ascii. */
-bool char_is_ascii(char character);
+[[nodiscard]] bool charIsAscii(char character);
-FudStatus utf8_is_ascii(FudUtf8& character, bool& isAscii);
+[[nodiscard]] bool utf8IsAscii(FudUtf8 character);
/** \brief Checks if a character is alphanumeric. */
-bool char_is_alphanumeric(char character);
+[[nodiscard]] bool charIsAlphanumeric(char character);
/** \brief Checks if a character is alphanumeric. */
-FudStatus utf8_is_alphanumeric(FudUtf8* character, bool* pred);
+[[nodiscard]] bool utf8IsAlphanumeric(FudUtf8 character);
/** \brief Checks if a character is alphabetic. */
-bool char_is_alpha(char character);
+[[nodiscard]] bool charIsAlpha(char character);
/** \brief Checks if a character is alphabetic. */
-FudStatus utf8_is_alpha(FudUtf8* character, bool* pred);
+[[nodiscard]] bool utf8IsAlpha(FudUtf8 character);
/** \brief Checks if a character is lowercase. */
-bool char_is_lowercase(char character);
+[[nodiscard]] bool charIsLowercase(char character);
/** \brief Checks if a character is lowercase. */
-FudStatus utf8_is_lowercase(FudUtf8* character, bool* pred);
+[[nodiscard]] bool utf8IsLowercase(FudUtf8 character);
/** \brief Checks if a character is an uppercase character. */
-bool char_is_uppercase(char character);
+[[nodiscard]] bool charIsUppercase(char character);
/** \brief Checks if a character is uppercase. */
-FudStatus utf8_is_uppercase(FudUtf8* character, bool* pred);
+[[nodiscard]] bool utf8IsUppercase(FudUtf8 character);
/** \brief Checks if a character is a digit. */
-bool char_is_digit(char character);
+[[nodiscard]] bool charIsDigit(char character);
/** \brief Checks if a character is a digit. */
-FudStatus utf8_is_digit(FudUtf8* character, bool* pred);
+[[nodiscard]] bool utf8IsDigit(FudUtf8 character);
/** \brief Checks if a character is a hexadecimal character. */
-bool char_is_hex_digit(char character);
+[[nodiscard]] bool charIsHexDigit(char character);
/** \brief Checks if a character is a hexadecimal digit. */
-FudStatus utf8_is_hex_digit(FudUtf8* character, bool* pred);
+[[nodiscard]] bool utf8IsHexDigit(FudUtf8 character);
/** \brief Checks if a character is a control character. */
-bool char_is_control(char character);
+[[nodiscard]] bool charIsControl(char character);
/** \brief Checks if a character is a control character. */
-FudStatus utf8_is_control(FudUtf8* character, bool* pred);
+[[nodiscard]] bool utf8IsControl(FudUtf8 character);
/** \brief Checks if a character is a graphical character. */
-bool char_is_graphical(char character);
+[[nodiscard]] bool charIsGraphical(char character);
/** \brief Checks if a character is a graphical character. */
-FudStatus utf8_is_graphical(FudUtf8* character, bool* pred);
+[[nodiscard]] bool utf8IsGraphical(FudUtf8 character);
/** \brief Checks if a character is a space character. */
-bool char_is_space(char character);
+[[nodiscard]] bool charIsSpace(char character);
/** \brief Checks if a character is a space character. */
-FudStatus utf8_is_space(FudUtf8* character, bool* pred);
+[[nodiscard]] bool utf8IsSpace(FudUtf8 character);
/** \brief Checks if a character is a blank character. */
-bool char_is_blank(char character);
+[[nodiscard]] bool charIsBlank(char character);
/** \brief Checks if a character is a blank character. */
-FudStatus utf8_is_blank(FudUtf8* character, bool* pred);
+[[nodiscard]] bool utf8IsBlank(FudUtf8 character);
/** \brief Checks if a character is a printable character. */
-bool char_is_printable(char character);
+[[nodiscard]] bool charIsPrintable(char character);
/** \brief Checks if a character is a printable character. */
-FudStatus utf8_is_printable(FudUtf8* character, bool* pred);
+[[nodiscard]] bool utf8IsPrintable(FudUtf8 character);
/** \brief Checks if a character is a punctuation character. */
-bool char_is_punctuation(char character);
+[[nodiscard]] bool charIsPunctuation(char character);
/** \brief Checks if a character is a punctuation character. */
-FudStatus utf8_is_punctuation(FudUtf8* character, bool* pred);
+[[nodiscard]] bool utf8IsPunctuation(FudUtf8 character);
-uint8_t char_to_lower(uint8_t character);
+/** \brief Converts character to lowercase if valid. */
+uint8_t charToLower(uint8_t character);
-FudUtf8* utf8_to_lower(FudUtf8* character);
+/** \brief Converts character to lowercase if valid. */
+FudUtf8 utf8ToLower(FudUtf8 character);
-uint8_t char_to_upper(uint8_t character);
+/** \brief Converts character to uppercase if valid. */
+uint8_t charToUpper(uint8_t character);
-FudUtf8* utf8_to_upper(FudUtf8* character);
+/** \brief Converts character to uppercase if valid. */
+FudUtf8 utf8ToUpper(FudUtf8 character);
} // namespace fud
diff --git a/include/fud_vector.hpp b/include/fud_vector.hpp
new file mode 100644
index 0000000..56e1659
--- /dev/null
+++ b/include/fud_vector.hpp
@@ -0,0 +1,64 @@
+/*
+ * 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_VECTOR_HPP
+#define FUD_VECTOR_HPP
+
+#include "fud_allocator.hpp"
+#include "fud_result.hpp"
+#include "fud_status.hpp"
+
+#include <cstddef>
+
+namespace fud {
+
+template <typename T>
+class Vector {
+ public:
+ static Result<Vector<T>, FudStatus> from(const Vector<T>& rhs);
+
+ static Vector<T> move(Vector<T>&& rhs);
+
+ FudStatus copy(const Vector<T>& rhs);
+
+ FudStatus take(Vector<T>&& rhs);
+
+ [[nodiscard]] size_t size() const {
+ return m_length;
+ }
+
+ [[nodiscard]] size_t capacity() const {
+ return m_capacity;
+ }
+
+ FudStatus reserve();
+
+ FudStatus resize();
+
+ FudStatus clear();
+
+ // FudResult at();
+
+ private:
+ Allocator* m_allocator{&globalFudAllocator};
+ size_t m_length{0};
+ size_t m_capacity{0};
+};
+
+} // namespace fud
+
+#endif
diff --git a/source/fud_assert.cpp b/source/fud_assert.cpp
index 98f17d0..3df6734 100644
--- a/source/fud_assert.cpp
+++ b/source/fud_assert.cpp
@@ -1,10 +1,11 @@
#include "fud_assert.hpp"
-#include "fud_array.hpp"
+// #include "fud_array.hpp"
#include <climits>
#include <cstdio>
-#include <format>
+#include <exception>
+// #include <format>
namespace fud {
@@ -20,15 +21,18 @@ constexpr auto MAX_LINE_CHARS = BITS_PER_OCTAL * sizeof(decltype(std::source_loc
fputs(file_name, stderr);
}
+ /*
constexpr std::size_t assertMsgSize = MAX_LINE_CHARS + 3;
Array<char, assertMsgSize> buffer{};
static_cast<void>(std::format_to_n(buffer.data(), buffer.size() - 1U, ":{}:", sourceLocation.line()));
buffer[buffer.size() - 1] = '\0';
fputs(buffer.data(), stderr);
+ */
fputs(sourceLocation.function_name(), stderr);
fputs(": ", stderr);
fputs(assertion, stderr);
+ fputc('\n', stderr);
std::terminate();
}
diff --git a/source/fud_format.cpp b/source/fud_format.cpp
new file mode 100644
index 0000000..ab1bb4f
--- /dev/null
+++ b/source/fud_format.cpp
@@ -0,0 +1,14 @@
+// #include "fud_format.hpp"
+
+namespace fud {
+
+/*
+Result<FormatSpec, FudStatus> FormatSpec::make(StringView view, size_t& length)
+{
+ static_cast<void>(view);
+ static_cast<void>(length);
+ return FudStatus::NotImplemented;
+}
+*/
+
+} // namespace fud
diff --git a/source/fud_string.cpp b/source/fud_string.cpp
index d354fe7..b714dfc 100644
--- a/source/fud_string.cpp
+++ b/source/fud_string.cpp
@@ -23,31 +23,6 @@
namespace fud {
-ssize_t cStringLength(const char* str)
-{
- constexpr auto maxLength = SSIZE_MAX - 1;
- return cStringLength(str, maxLength);
-}
-
-ssize_t cStringLength(const char* str, size_t maxLength)
-{
- if (str == nullptr || maxLength > (SSIZE_MAX - 1)) {
- return -1;
- }
-
- ssize_t size = 0;
-
- while (str[size] != 0 && static_cast<size_t>(size) < maxLength) {
- size++;
- }
-
- if (str[size] != 0 && static_cast<size_t>(size) == maxLength) {
- return static_cast<ssize_t>(maxLength) + 1;
- }
-
- return size;
-}
-
StringResult String::makeFromCString(const char* cString)
{
return makeFromCString(cString, &globalFudAllocator);
diff --git a/source/fud_string_view.cpp b/source/fud_string_view.cpp
index 23a4671..fdb63b3 100644
--- a/source/fud_string_view.cpp
+++ b/source/fud_string_view.cpp
@@ -61,7 +61,7 @@ Result<size_t, FudStatus> StringView::skipWhitespace()
return RetType::error(FudStatus::NullPointer);
}
size_t index = 0;
- while (m_length > 0 && char_is_space(static_cast<char>(m_data[0]))) {
+ while (m_length > 0 && charIsSpace(static_cast<char>(m_data[0]))) {
m_data++;
m_length--;
index++;
@@ -78,7 +78,7 @@ Result<size_t, FudStatus> StringView::trimWhitespace()
}
size_t count = 0;
- while (m_length > 0 && char_is_space(static_cast<char>(m_data[m_length - 1]))) {
+ while (m_length > 0 && charIsSpace(static_cast<char>(m_data[m_length - 1]))) {
m_length--;
count++;
}
@@ -86,6 +86,40 @@ Result<size_t, FudStatus> StringView::trimWhitespace()
return RetType::okay(count);
}
+bool StringView::advance()
+{
+ if (m_length < 1) {
+ return false;
+ }
+ m_length--;
+ m_data++;
+ return true;
+}
+
+void StringView::advanceUnsafe()
+{
+ fudAssert(m_length > 0);
+ m_length--;
+ m_data++;
+}
+
+bool StringView::advance(size_t size)
+{
+ if (size > m_length) {
+ return false;
+ }
+ m_length -= size;
+ m_data += size;
+ return true;
+}
+
+void StringView::advanceUnsafe(size_t size)
+{
+ fudAssert(size <= m_length);
+ m_length -= size;
+ m_data += size;
+}
+
#if 0
FudStatus fud_string_truncate(ExtBasicString* source, ssize_t newLength)
diff --git a/source/fud_utf8.cpp b/source/fud_utf8.cpp
index ee8137a..4d617da 100644
--- a/source/fud_utf8.cpp
+++ b/source/fud_utf8.cpp
@@ -19,26 +19,19 @@
#include "fud_string.hpp"
-#include <new> // IWYU pragma: keep - this is for placement new overloads.
-
namespace fud {
-FudUtf8 FudUtf8::fromString(const String& fudString, size_t index) noexcept
+FudUtf8 FudUtf8::from(const String& fudString, size_t index) noexcept
{
if (!fudString.valid()) {
return invalidAscii();
}
- return fromStringView(StringView{fudString}, index);
+ return from(StringView{fudString}, index);
}
-// FudUtf8 FudUtf8::fromStringView(const StringView& view, size_t index) noexcept
-// {
-// return fromStringView(StringView{view}, index);
-// }
-
-FudUtf8 FudUtf8::fromStringView(StringView view, size_t index) noexcept
+FudUtf8 FudUtf8::from(StringView view, size_t index) noexcept
{
auto viewLocal{view};
auto len = viewLocal.length();
@@ -76,126 +69,113 @@ FudUtf8 FudUtf8::fromStringView(StringView view, size_t index) noexcept
return invalidAscii();
}
-bool char_is_ascii(char character)
+bool charIsAscii(char character)
{
return static_cast<uint8_t>(character & ~ASCII_MASK) == 0;
}
-FudStatus utf8_is_ascii(FudUtf8* character, bool* isAscii)
+bool utf8IsAscii(FudUtf8 character)
{
- if (anyAreNull(character, isAscii)) {
- return FudStatus::NullPointer;
- }
-
- *isAscii = character->getType() == Utf8Type::Ascii && character->valid();
-
- return FudStatus::Success;
+ return character.getType() == Utf8Type::Ascii && character.valid();
}
namespace impl {
-/* Assumes that predicate is not a null pointer! */
template <typename Predicate>
-FudStatus isAsciiPredicate(FudUtf8* character, bool* pred, Predicate&& predicate)
+bool isAsciiPredicate(FudUtf8 character, Predicate&& predicate)
{
- if (anyAreNull(character, pred)) {
- return FudStatus::NullPointer;
- }
-
- auto maybeAscii = character->getAscii();
+ auto maybeAscii = character.getAscii();
if (!maybeAscii.has_value()) {
- return FudStatus::ArgumentInvalid;
+ return false;
}
auto asciiChar = *maybeAscii;
- *pred = std::forward<Predicate>(predicate)(asciiChar.asChar());
-
- return FudStatus::Success;
+ return std::forward<Predicate>(predicate)(asciiChar.asChar());
}
} // namespace impl
-bool char_is_alphanumeric(char character)
+bool charIsAlphanumeric(char character)
{
- if (!char_is_ascii(character)) {
+ if (!charIsAscii(character)) {
return false;
}
- if (char_is_alpha(character)) {
+ if (charIsAlpha(character)) {
return true;
}
- return char_is_digit(character);
+ return charIsDigit(character);
}
-FudStatus utf8_is_alphanumeric(FudUtf8* character, bool* pred)
+bool utf8IsAlphanumeric(FudUtf8 character)
{
- return impl::isAsciiPredicate(character, pred, char_is_alphanumeric);
+ return impl::isAsciiPredicate(character, charIsAlphanumeric);
}
-bool char_is_alpha(char character)
+bool charIsAlpha(char character)
{
- if (!char_is_ascii(character)) {
+ if (!charIsAscii(character)) {
return false;
}
- if (char_is_uppercase(character)) {
+ if (charIsUppercase(character)) {
return true;
}
- return char_is_lowercase(character);
+ return charIsLowercase(character);
}
-FudStatus utf8_is_alpha(FudUtf8* character, bool* pred)
+bool utf8IsAlpha(FudUtf8 character)
{
- return impl::isAsciiPredicate(character, pred, char_is_alpha);
+ return impl::isAsciiPredicate(character, charIsAlpha);
}
-bool char_is_lowercase(char character)
+bool charIsLowercase(char character)
{
- if (!char_is_ascii(character)) {
+ if (!charIsAscii(character)) {
return false;
}
return 'a' <= character && character <= 'z';
}
-FudStatus utf8_is_lowercase(FudUtf8* character, bool* pred)
+bool utf8IsLowercase(FudUtf8 character)
{
- return impl::isAsciiPredicate(character, pred, char_is_lowercase);
+ return impl::isAsciiPredicate(character, charIsLowercase);
}
-bool char_is_uppercase(char character)
+bool charIsUppercase(char character)
{
- if (!char_is_ascii(character)) {
+ if (!charIsAscii(character)) {
return false;
}
return 'A' <= character && character <= 'Z';
}
-FudStatus utf8_is_uppercase(FudUtf8* character, bool* pred)
+bool utf8IsUppercase(FudUtf8 character)
{
- return impl::isAsciiPredicate(character, pred, char_is_uppercase);
+ return impl::isAsciiPredicate(character, charIsUppercase);
}
-bool char_is_digit(char character)
+bool charIsDigit(char character)
{
- if (!char_is_ascii(character)) {
+ if (!charIsAscii(character)) {
return false;
}
return '0' <= character && character <= '9';
}
-FudStatus utf8_is_digit(FudUtf8* character, bool* pred)
+bool utf8IsDigit(FudUtf8 character)
{
- return impl::isAsciiPredicate(character, pred, char_is_digit);
+ return impl::isAsciiPredicate(character, charIsDigit);
}
-bool char_is_hex_digit(char character)
+bool charIsHexDigit(char character)
{
- if (!char_is_ascii(character)) {
+ if (!charIsAscii(character)) {
return false;
}
@@ -203,14 +183,14 @@ bool char_is_hex_digit(char character)
('A' <= character && character <= 'F');
}
-FudStatus utf8_is_hex_digit(FudUtf8* character, bool* pred)
+bool utf8IsHexDigit(FudUtf8 character)
{
- return impl::isAsciiPredicate(character, pred, char_is_hex_digit);
+ return impl::isAsciiPredicate(character, charIsHexDigit);
}
-bool char_is_control(char character)
+bool charIsControl(char character)
{
- if (!char_is_ascii(character)) {
+ if (!charIsAscii(character)) {
return false;
}
@@ -219,70 +199,70 @@ bool char_is_control(char character)
return ((static_cast<uint8_t>(character) <= maxControlChar)) || character == deleteChar;
}
-FudStatus utf8_is_control(FudUtf8* character, bool* pred)
+bool utf8IsControl(FudUtf8 character)
{
- return impl::isAsciiPredicate(character, pred, char_is_control);
+ return impl::isAsciiPredicate(character, charIsControl);
}
-bool char_is_graphical(char character)
+bool charIsGraphical(char character)
{
- if (!char_is_ascii(character)) {
+ if (!charIsAscii(character)) {
return false;
}
- return char_is_alphanumeric(character) || char_is_punctuation(character);
+ return charIsAlphanumeric(character) || charIsPunctuation(character);
}
-FudStatus utf8_is_graphical(FudUtf8* character, bool* pred)
+bool utf8IsGraphical(FudUtf8 character)
{
- return impl::isAsciiPredicate(character, pred, char_is_graphical);
+ return impl::isAsciiPredicate(character, charIsGraphical);
}
-bool char_is_space(char character)
+bool charIsSpace(char character)
{
- if (!char_is_ascii(character)) {
+ if (!charIsAscii(character)) {
return false;
}
return character == ' ' || character == '\t' || character == '\n' || character == '\r' || character == '\v';
}
-FudStatus utf8_is_space(FudUtf8* character, bool* pred)
+bool utf8IsSpace(FudUtf8 character)
{
- return impl::isAsciiPredicate(character, pred, char_is_space);
+ return impl::isAsciiPredicate(character, charIsSpace);
}
-bool char_is_blank(char character)
+bool charIsBlank(char character)
{
- if (!char_is_ascii(character)) {
+ if (!charIsAscii(character)) {
return false;
}
return character == ' ' || character == '\t';
}
-FudStatus utf8_is_blank(FudUtf8* character, bool* pred)
+bool utf8IsBlank(FudUtf8 character)
{
- return impl::isAsciiPredicate(character, pred, char_is_blank);
+ return impl::isAsciiPredicate(character, charIsBlank);
}
-bool char_is_printable(char character)
+bool charIsPrintable(char character)
{
- if (!char_is_ascii(character)) {
+ if (!charIsAscii(character)) {
return false;
}
return (character >= ' ' && character <= '~');
}
-FudStatus utf8_is_printable(FudUtf8* character, bool* pred)
+bool utf8IsPrintable(FudUtf8 character)
{
- return impl::isAsciiPredicate(character, pred, char_is_printable);
+ return impl::isAsciiPredicate(character, charIsPrintable);
}
-bool char_is_punctuation(char character)
+bool charIsPunctuation(char character)
{
- if (!char_is_ascii(character)) {
+ if (!charIsAscii(character)) {
return false;
}
@@ -290,14 +270,14 @@ bool char_is_punctuation(char character)
(character >= '[' && character <= '`') || (character >= '{' && character <= '~');
}
-FudStatus utf8_is_punctuation(FudUtf8* character, bool* pred)
+bool utf8IsPunctuation(FudUtf8 character)
{
- return impl::isAsciiPredicate(character, pred, char_is_punctuation);
+ return impl::isAsciiPredicate(character, charIsPunctuation);
}
-uint8_t char_to_lower(uint8_t character)
+uint8_t charToLower(uint8_t character)
{
- if (char_is_uppercase(static_cast<char>(character))) {
+ if (charIsUppercase(static_cast<char>(character))) {
constexpr uint8_t lowerA = 'a';
constexpr uint8_t upperA = 'A';
return static_cast<uint8_t>(character - upperA) + lowerA;
@@ -305,22 +285,18 @@ uint8_t char_to_lower(uint8_t character)
return character;
}
-FudUtf8* utf8_to_lower(FudUtf8* character)
+FudUtf8 utf8ToLower(FudUtf8 character)
{
- if (character == nullptr) {
- return character;
- }
-
- static_cast<void>(character->transformAscii([](Ascii& ascii) {
- ascii = Ascii{char_to_lower(static_cast<uint8_t>(ascii.asChar()))};
+ static_cast<void>(character.transformAscii([](Ascii& ascii) {
+ ascii = Ascii{charToLower(static_cast<uint8_t>(ascii.asChar()))};
}));
return character;
}
-uint8_t char_to_upper(uint8_t character)
+uint8_t charToUpper(uint8_t character)
{
- if (char_is_lowercase(static_cast<char>(character))) {
+ if (charIsLowercase(static_cast<char>(character))) {
constexpr uint8_t lowerA = 'a';
constexpr uint8_t upperA = 'A';
return static_cast<uint8_t>(character - lowerA) + upperA;
@@ -328,14 +304,10 @@ uint8_t char_to_upper(uint8_t character)
return character;
}
-FudUtf8* utf8_to_upper(FudUtf8* character)
+FudUtf8 utf8ToUpper(FudUtf8 character)
{
- if (character == nullptr) {
- return character;
- }
-
- static_cast<void>(character->transformAscii([](Ascii& ascii) {
- ascii = Ascii{char_to_upper(static_cast<uint8_t>(ascii.asChar()))};
+ static_cast<void>(character.transformAscii([](Ascii& ascii) {
+ ascii = Ascii{charToUpper(static_cast<uint8_t>(ascii.asChar()))};
}));
return character;
diff --git a/source/fud_utf8_iterator.cpp b/source/fud_utf8_iterator.cpp
index 2557dc0..a815c64 100644
--- a/source/fud_utf8_iterator.cpp
+++ b/source/fud_utf8_iterator.cpp
@@ -25,7 +25,7 @@ std::optional<FudUtf8> Utf8Iterator::peek() const
return std::nullopt;
}
- auto character = FudUtf8::fromStringView(m_view, m_index);
+ auto character = FudUtf8::from(m_view, m_index);
if (!character.valid()) {
return std::nullopt;
@@ -41,7 +41,7 @@ std::optional<FudUtf8> Utf8Iterator::next()
return std::nullopt;
}
- auto character = FudUtf8::fromStringView(m_view, m_index);
+ auto character = FudUtf8::from(m_view, m_index);
if (!character.valid()) {
m_index = m_view.length();
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 0860c0d..c4d957b 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -59,23 +59,17 @@ endfunction()
fud_add_test(test_fud SOURCES test_fud.cpp)
fud_add_test(test_allocator SOURCES test_allocator.cpp)
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_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_deserialize_number SOURCES test_deserialize_number.cpp)
# fud_add_test(test_ext_algorithm SOURCES test_algorithm.cpp)
# fud_add_test(test_ext_array SOURCES
# test_ext_array.cpp
# test_ext_unique_array.cpp)
-# fud_add_test(test_ext_utf8 SOURCES
-# test_ext_utf8.cpp)
-# fud_add_test(test_ext_string SOURCES
-# test_ext_string.cpp
-# test_ext_string_cxx.cpp)
-# fud_add_test(test_ext_string_format SOURCES
-# test_ext_string_format.cpp)
-
-# fud_add_test(test_c_file SOURCES test_c_file.cpp)
diff --git a/test/test_common.hpp b/test/test_common.hpp
index 05f86db..0ca8eb4 100644
--- a/test/test_common.hpp
+++ b/test/test_common.hpp
@@ -34,7 +34,16 @@ static_assert(sizeof(THREE_BYTE) == 3 + 1);
static_assert(sizeof(FOUR_BYTE) == 4 + 1);
#define CHQUOTE "why waste time learning, when ignorance is instantaneous?"
-#define CHARACTER_SET "abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+
+#define LOWERCASE_CHARS "abcdefghijklmnopqrstuvwxyz"
+#define UPPERCASE_CHARS "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+#define DECIMAL_CHARS "0123456789"
+#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 CHARACTER_SET LOWERCASE_CHARS " " UPPERCASE_CHARS
+
// NOLINTEND(cppcoreguidelines-macro-usage)
constexpr size_t charSetSize = sizeof(CHARACTER_SET) - 1;
diff --git a/test/test_format.cpp b/test/test_format.cpp
index a373fec..319ed22 100644
--- a/test/test_format.cpp
+++ b/test/test_format.cpp
@@ -15,21 +15,17 @@
* limitations under the License.
*/
-#include "fud_array.hpp"
-#include "fud_format.hpp"
-#include "fud_span.hpp"
+// #include "fud_array.hpp"
+// #include "fud_format.hpp"
+// #include "fud_span.hpp"
#include "gtest/gtest.h"
namespace fud {
-TEST(FormatTest, BasicTest)
+TEST(FormatTest, FormatSpecTest)
{
- auto buffer{Array<char, 64>::constFill('\0')};
- auto span = Span<char, buffer.size() - 1U>::makeCStringBuffer(buffer);
- auto formatResult = format(span, "Hello, {}! {}", "world", 42);
- printf("%s\n", buffer.data());
}
} // namespace fud
diff --git a/test/test_utf8.cpp b/test/test_utf8.cpp
new file mode 100644
index 0000000..8f1d655
--- /dev/null
+++ b/test/test_utf8.cpp
@@ -0,0 +1,1163 @@
+/*
+ * 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_algorithm.hpp"
+#include "fud_allocator.hpp"
+#include "fud_array.hpp"
+#include "fud_string.hpp"
+#include "fud_utf8.hpp"
+#include "fud_utf8_iterator.hpp"
+#include "fud_vector.hpp"
+#include "test_common.hpp"
+// #include "fud_format.hpp"
+// #include "fud_span.hpp"
+
+#include "gtest/gtest.h"
+
+namespace fud {
+
+constexpr size_t validAsciiSize = INT8_MAX + 1;
+constexpr size_t invalidAsciiSize = UINT8_MAX + 1 - validAsciiSize;
+
+constexpr size_t numControlChars = 33;
+constexpr char printableCharOffset = 0x20;
+
+constexpr auto invalidAscii = FudUtf8::invalidAsciiCode.character();
+
+auto generateInvalidAsciiChars()
+{
+ Iota<utf8> iota{};
+ return generate([]() { return Array<utf8, invalidAsciiSize>{}; }, [&]() { return iota().value(); });
+}
+
+TEST(Utf8Test, Utf8Creation)
+{
+ const Array<utf8, 4> threeByte = {THREE_BYTE};
+
+ FudUtf8 utf8Point{FudUtf8::make(threeByte)};
+ ASSERT_NE(utf8Point.data(), nullptr);
+ ASSERT_EQ(utf8Point.size(), 3);
+ ASSERT_NE(utf8Point.hash(), -1);
+
+ const Array<utf8, 4> asciiLetter = {'A'};
+ utf8Point = FudUtf8::make(asciiLetter);
+ ASSERT_NE(utf8Point.data(), nullptr);
+ ASSERT_EQ(utf8Point.size(), 1);
+
+ const Array<utf8, 4> twoByte = {TWO_BYTE};
+ utf8Point = FudUtf8::make(twoByte);
+ ASSERT_NE(utf8Point.data(), nullptr);
+ ASSERT_EQ(utf8Point.size(), 2);
+ ASSERT_NE(utf8Point.hash(), -1);
+
+ Array<utf8, 4> fourByte = {
+ static_cast<utf8>(FOUR_BYTE[0]),
+ static_cast<utf8>(FOUR_BYTE[1]),
+ static_cast<utf8>(FOUR_BYTE[2]),
+ static_cast<utf8>(FOUR_BYTE[3])};
+ utf8Point = FudUtf8::make(fourByte);
+ ASSERT_NE(utf8Point.data(), nullptr);
+ ASSERT_EQ(utf8Point.size(), 4);
+ ASSERT_NE(utf8Point.hash(), -1);
+
+ const Array<utf8, 4> invalidBytes = {0xFF, 0xFF, 0xFF, 0xFF};
+ utf8Point = FudUtf8::make(invalidBytes);
+ ASSERT_EQ(utf8Point.data(), nullptr);
+ ASSERT_EQ(utf8Point.size(), 0);
+ ASSERT_EQ(utf8Point.hash(), -1);
+}
+
+TEST(Utf8Test, Utf8MultiByte)
+{
+ Array<utf8, sizeof(MULTI_BYTE_LITERAL)> data{MULTI_BYTE_LITERAL};
+ constexpr size_t bufSize = data.size();
+ EXPECT_EQ(data[bufSize - 1], '\0');
+
+ class FixedAllocator final : public Allocator {
+ private:
+ Array<utf8, bufSize> m_memory{};
+ size_t m_allocated{0};
+
+ public:
+ virtual ~FixedAllocator() override final = default;
+
+ virtual Result<void*, FudStatus> allocate(size_t bytes, size_t alignment) override final
+ {
+ static_cast<void>(alignment);
+ if (bytes > m_memory.size() - m_allocated) {
+ return FudStatus::AllocFailure;
+ }
+ auto* data = m_memory.data() + m_allocated;
+ m_allocated += bytes;
+ return data;
+ }
+
+ virtual FudStatus deallocate(void* pointer, size_t bytes) override final
+ {
+ static_cast<void>(pointer);
+ static_cast<void>(bytes);
+ return FudStatus::Success;
+ }
+
+ virtual bool isEqual(const Allocator& rhs) const override final
+ {
+ return &rhs == this;
+ }
+ };
+ FixedAllocator fixedAllocator;
+
+ auto stringBufferRes{String::makeFromCString(MULTI_BYTE_LITERAL, &fixedAllocator)};
+
+ ASSERT_TRUE(stringBufferRes.isOkay());
+ auto stringBuffer{stringBufferRes.takeOkay()};
+ EXPECT_EQ(stringBuffer.size(), bufSize);
+ EXPECT_EQ(stringBuffer.size(), sizeof(data));
+ EXPECT_EQ(stringBuffer.length(), bufSize - 1);
+ EXPECT_TRUE(stringBuffer.nullTerminated());
+ EXPECT_TRUE(stringBuffer.valid());
+ ASSERT_TRUE(stringBuffer.utf8Valid());
+
+ Utf8Iterator utf8Iter{stringBuffer};
+ auto characterOpt = utf8Iter.next();
+ ASSERT_TRUE(characterOpt.has_value());
+
+ // MULTI_BYTE_LITERAL "test今日素敵はですねƩ®😀z"
+ const Array<FudUtf8, 16> multiByteCharacters{
+ FudUtf8::make(Utf8Variant{Ascii{'t'}}),
+ FudUtf8::make(Utf8Variant{Ascii{'e'}}),
+ FudUtf8::make(Utf8Variant{Ascii{'s'}}),
+ FudUtf8::make(Utf8Variant{Ascii{'t'}}),
+ FudUtf8::from(StringView{sizeof("今"), "今"}, 0),
+ FudUtf8::from(StringView{sizeof("日"), "日"}, 0),
+ FudUtf8::from(StringView{sizeof("素"), "素"}, 0),
+ FudUtf8::from(StringView{sizeof("敵"), "敵"}, 0),
+ FudUtf8::from(StringView{sizeof("は"), "は"}, 0),
+ FudUtf8::from(StringView{sizeof("で"), "で"}, 0),
+ FudUtf8::from(StringView{sizeof("す"), "す"}, 0),
+ FudUtf8::from(StringView{sizeof("ね"), "ね"}, 0),
+ FudUtf8::from(StringView{sizeof("Ʃ"), "Ʃ"}, 0),
+ FudUtf8::from(StringView{sizeof("®"), "®"}, 0),
+ FudUtf8::from(StringView{sizeof("😀"), "😀"}, 0),
+ FudUtf8::make(Utf8Variant{Ascii{'z'}}),
+ };
+
+ size_t idx = 0;
+ while (characterOpt.has_value()) {
+ auto character = *characterOpt;
+ if (character != FudUtf8{Utf8Variant{Ascii{'\0'}}}) {
+ EXPECT_TRUE(character.size() >= 1);
+ ASSERT_LT(idx, multiByteCharacters.size());
+ EXPECT_EQ(character.size(), multiByteCharacters[idx].size());
+ EXPECT_EQ(character, multiByteCharacters[idx]);
+ EXPECT_TRUE(multiByteCharacters[idx].valid());
+ if (character != multiByteCharacters[idx]) {
+ printf("idx = %zu, %.*s\n", idx, static_cast<int>(character.size()), character.data());
+ }
+ idx++;
+ }
+ characterOpt = utf8Iter.next();
+ }
+ utf8Iter.reset();
+ ASSERT_TRUE(utf8Iter.next().has_value());
+
+ FudUtf8 invalid = FudUtf8::invalidAscii();
+ ASSERT_FALSE(invalid.valid());
+ ASSERT_EQ(invalid.size(), 0);
+ ASSERT_EQ(invalid.data(), nullptr);
+ ASSERT_EQ(invalid.hash(), -1);
+}
+
+TEST(Utf8Test, Utf8IsAscii)
+{
+ ASSERT_FALSE(charIsAscii(invalidAscii));
+
+ 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));
+
+ 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));
+
+ 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));
+
+ 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));
+}
+
+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{};
+ for (char idx = 0; idx < INT8_MAX; ++idx) {
+ if (!alphaNumericSet.isKey(idx)) {
+ ASSERT_TRUE(nonAlphaNumericChars.pushBack(idx));
+ }
+ }
+ ASSERT_FALSE(anyOf(nonAlphaNumericChars, charIsAlphanumeric));
+
+ auto invalidAsciiChars = generateInvalidAsciiChars();
+ 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
+}
+
+#if 0
+TEST(Utf8Test, Utf8IsAlpha)
+{
+ constexpr size_t numAlphaChars = sizeof(ALPHA_CHARS) - 1;
+ Array<char, numAlphaChars + 1> alphaCharLiteral{ALPHA_CHARS};
+ 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())};
+
+ constexpr size_t numNonAlphaChars = validAsciiSize - numAlphaChars;
+ FixedVector<char, numNonAlphaChars> nonAlphaChars{};
+ for (char idx = 0; idx < INT8_MAX; ++idx) {
+ if (!alphaSet.isKey(idx)) {
+ ASSERT_TRUE(nonAlphaChars.pushBack(idx));
+ }
+ }
+ 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;
+ }));
+}
+
+TEST(Utf8Test, Utf8IsLower)
+{
+ constexpr size_t numLowerChars = 26;
+ Array<char, numLowerChars + 1> lowerCharLiteral{LOWERCASE_CHARS};
+ 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())};
+
+ constexpr size_t numNonLowerChars = validAsciiSize - numLowerChars;
+ FixedVector<char, numNonLowerChars> nonLowerChars{};
+ for (char idx = 0; idx < INT8_MAX; ++idx) {
+ if (!lowerSet.isKey(idx)) {
+ ASSERT_TRUE(nonLowerChars.pushBack(idx));
+ }
+ }
+ 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;
+ }));
+}
+
+TEST(Utf8Test, Utf8IsUpper)
+{
+ constexpr size_t numUpperChars = 26;
+ Array<char, numUpperChars + 1> upperCharLiteral{UPPERCASE_CHARS};
+ 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())};
+
+ constexpr size_t numNonUpperChars = validAsciiSize - numUpperChars;
+ FixedVector<char, numNonUpperChars> nonUpperChars{};
+ for (char idx = 0; idx < INT8_MAX; ++idx) {
+ if (!upperSet.isKey(idx)) {
+ ASSERT_TRUE(nonUpperChars.pushBack(idx));
+ }
+ }
+ 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;
+ }));
+}
+
+TEST(Utf8Test, Utf8IsDigit)
+{
+ constexpr size_t numDigitChars = 10;
+ Array<char, numDigitChars + 1> digitCharLiteral{"0123456789"};
+ 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())};
+
+ constexpr size_t numNonDigitChars = validAsciiSize - numDigitChars;
+ FixedVector<char, numNonDigitChars> nonDigitChars{};
+ for (char idx = 0; idx < INT8_MAX; ++idx) {
+ if (!digitSet.isKey(idx)) {
+ ASSERT_TRUE(nonDigitChars.pushBack(idx));
+ }
+ }
+ 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;
+ }));
+}
+
+TEST(Utf8Test, Utf8IsHexDigit)
+{
+ constexpr size_t numHexDigitChars = 6 * 2 + 10;
+ Array<char, numHexDigitChars + 1> hexDigitCharLiteral{"abcdefABCDEF0123456789"};
+ 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())};
+
+ constexpr size_t numNonHexDigitChars = validAsciiSize - numHexDigitChars;
+ FixedVector<char, numNonHexDigitChars> nonHexDigitChars{};
+ for (char idx = 0; idx < INT8_MAX; ++idx) {
+ if (!hexDigitSet.isKey(idx)) {
+ ASSERT_TRUE(nonHexDigitChars.pushBack(idx));
+ }
+ }
+ 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;
+ }));
+}
+
+TEST(Utf8Test, Utf8IsControl)
+{
+ auto controlChars = generateIndexArray<Array, char, numControlChars>([](int idx) { return static_cast<char>(idx); });
+ 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;
+ }));
+}
+
+TEST(Utf8Test, Utf8IsGraphical)
+{
+ constexpr size_t numGraphicalChars = sizeof(GRAPHICAL_CHARS) - 1;
+ Array<char, numGraphicalChars + 1> graphicalCharLiteral{GRAPHICAL_CHARS};
+ 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())};
+
+ 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));
+ }
+ }
+ 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;
+ }));
+}
+
+TEST(Utf8Test, Utf8IsSpace)
+{
+ constexpr size_t numSpaceChars = sizeof(" \t\v\r\n") - 1;
+ Array<char, numSpaceChars + 1> spaceCharLiteral{" \t\v\r\n"};
+ 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())};
+
+ 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));
+ }
+ }
+ 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;
+ }));
+}
+
+TEST(Utf8Test, Utf8IsBlank)
+{
+ constexpr size_t numBlankChars = sizeof(" \t") - 1;
+ Array<char, numBlankChars + 1> blankCharLiteral{" \t"};
+ 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())};
+
+ 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));
+ }
+ }
+ 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;
+ }));
+}
+
+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));
+
+ auto printableSetResult{StaticSet<char, numPrintableChars>::makeFromArray(printableChars)};
+ ASSERT_TRUE(printableSetResult.isOkay());
+ auto printableSet{std::move(printableSetResult.getOkay())};
+
+ 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));
+ }
+ }
+ 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;
+ }));
+}
+
+TEST(Utf8Test, Utf8IsPunctuation)
+{
+ constexpr size_t numPunctuationChars = sizeof(PUNCTUATION_CHARS) - 1;
+ Array<char, numPunctuationChars + 1> punctuationCharLiteral{PUNCTUATION_CHARS};
+ 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())};
+
+ 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));
+ }
+ }
+ 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;
+ }));
+}
+#endif
+
+} // namespace fud