summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt2
-rw-r--r--include/fud_format.hpp131
-rw-r--r--include/fud_option.hpp45
-rw-r--r--include/fud_string_convert.hpp456
-rw-r--r--include/fud_string_view.hpp41
-rw-r--r--include/fud_utf8.hpp116
-rw-r--r--source/fud_assert.cpp1
-rw-r--r--source/fud_format.cpp515
-rw-r--r--source/fud_string.cpp2
-rw-r--r--source/fud_string_convert.cpp110
-rw-r--r--source/fud_string_view.cpp701
-rw-r--r--source/fud_utf8.cpp207
-rw-r--r--test/CMakeLists.txt1
-rw-r--r--test/test_format.cpp330
-rw-r--r--test/test_string_convert.cpp186
-rw-r--r--test/test_utf8.cpp219
-rwxr-xr-xtools/coverage.sh1
-rwxr-xr-xtools/create-pyenv.sh1
-rwxr-xr-xtools/run-cppcheck.sh1
19 files changed, 2091 insertions, 975 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 5a41872..12c0727 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -25,6 +25,7 @@ add_library(fud SHARED
source/fud_memory.cpp
source/fud_sqlite.cpp
source/fud_string_view.cpp
+ source/fud_string_convert.cpp
source/fud_string.cpp
source/fud_utf8.cpp
source/fud_utf8_iterator.cpp
@@ -109,6 +110,7 @@ set(FUD_HEADERS
"include/fud_span.hpp"
"include/fud_status.hpp"
"include/fud_string.hpp"
+ "include/fud_string_convert.hpp"
"include/fud_string_view.hpp"
"include/fud_sqlite.hpp"
"include/fud_unique_array.hpp"
diff --git a/include/fud_format.hpp b/include/fud_format.hpp
index ea32bd8..2576eca 100644
--- a/include/fud_format.hpp
+++ b/include/fud_format.hpp
@@ -22,24 +22,30 @@
#include "fud_result.hpp"
#include "fud_status.hpp"
#include "fud_string_view.hpp"
+#include "fud_option.hpp"
#include "fud_utf8.hpp"
#include <cstdint>
-#include <optional>
#include <variant>
+#include <limits>
namespace fud {
struct FormatAlign
{
- enum class Value : uint8_t
+ enum class Value : utf8
{
- Left,
- Right,
- Center
+ Left = '<',
+ Right = '>',
+ Center = '^',
+ Default = std::numeric_limits<utf8>::max()
};
- constexpr static std::optional<FormatAlign> from(utf8 letter)
+ constexpr static FormatAlign makeDefault() noexcept {
+ return {Value::Default};
+ }
+
+ constexpr static Option<FormatAlign> from(utf8 letter)
{
FormatAlign formatAlign;
switch (letter) {
@@ -53,12 +59,16 @@ struct FormatAlign
formatAlign.value = Value::Center;
break;
default:
- return std::nullopt;
+ return NullOpt;
}
return formatAlign;
}
+ Value operator()() const {
+ return value;
+ }
+
Value value;
};
@@ -66,37 +76,50 @@ struct FormatFill {
FormatAlign align;
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);
+ constexpr static FormatFill make() noexcept {
+ return FormatFill {FormatAlign::makeDefault(), ' '};
+ }
+
+ constexpr static Result<Option<FormatFill>, FudStatus> make(StringView formatView, size_t& length) noexcept {
+ using RetType = Result<Option<FormatFill>, FudStatus>;
+ if (formatView.length() < 1) {
+ return RetType::okay(NullOpt);
}
const auto* data = formatView.data();
- if (data[0] != 'A') {
- return RetType::okay(std::nullopt);
+ auto align1 = FormatAlign::from(data[0]);
+ decltype(align1) align2 = NullOpt;
+ if (formatView.length() > 1) {
+ align2 = FormatAlign::from(data[1]);
}
- auto align = FormatAlign::from(data[1]);
- if (!align.has_value()) {
- return FudStatus::FormatInvalid;
+ if (align2.hasValue()) {
+ length = 2;
+ auto fill = data[0];
+ if (not Ascii::valid(fill)) {
+ return FudStatus::Utf8Invalid;
+ }
+ if (fill == '{' || fill == '}') {
+ return FudStatus::FormatInvalid;
+ }
+ return RetType::okay(FormatFill{std::move(align2).value(), data[0]});
}
- auto fill = data[2];
- if (!Ascii::valid(fill)) {
- return FudStatus::Utf8Invalid;
+ if (align1.hasValue()) {
+ length = 1;
+ return RetType::okay(FormatFill{std::move(align1).value(), ' '});
}
- return RetType::okay(FormatFill{*align, fill});
+ return RetType::okay(NullOpt);
}
};
enum class FormatSign : uint8_t
{
- Plus,
- Minus,
- Space
+ Plus = '+',
+ Minus = '-',
+ Space = ' ',
+ Default = std::numeric_limits<utf8>::max()
};
enum class FormatStringType : uint8_t
@@ -155,6 +178,7 @@ enum class FormatPointerType : uint8_t
HexUpper
};
+/*
using FormatType = std::variant< // break
std::monostate,
FormatStringType,
@@ -163,33 +187,64 @@ using FormatType = std::variant< // break
FormatBoolType,
FormatFloatingType,
FormatPointerType>;
+*/
+
+enum class FormatType : utf8 {
+ Unspecified = '\0',
+ String = 's',
+ Escaped = '?',
+ BinaryLower = 'b',
+ BinaryUpper = 'B',
+ Character = 'c',
+ Decimal = 'd',
+ Octal = 'o',
+ HexLower = 'x',
+ HexUpper = 'X',
+ FloatHexLower = 'a',
+ FloatHexUpper = 'A',
+ ScientificLower = 'e',
+ ScientificUpper = 'E',
+ FixedLower = 'f',
+ FixedUpper = 'F',
+ GeneralLower = 'g',
+ GeneralUpper = 'G',
+};
struct FormatSpec;
using FormatSpecResult = Result<FormatSpec, FudStatus>;
struct FormatSpec {
- size_t width;
- size_t precision;
- FormatFill fill;
- FormatSign formatSign;
+ static constexpr uint32_t widthUnspecified = std::numeric_limits<uint32_t>::max();
+ static constexpr uint32_t precisionUnspecified = std::numeric_limits<uint32_t>::max();
+ static constexpr uint16_t positionUnspecified = std::numeric_limits<uint16_t>::max();
+ static constexpr utf8 openBracket = '{';
+ static constexpr utf8 closeBracket = '}';
+ static constexpr utf8 formatTypeUnspecified = std::numeric_limits<utf8>::max();
+ static constexpr utf8 localeChar = 'L';
+
+ uint32_t width{widthUnspecified};
+
+ uint32_t precision{precisionUnspecified};
+
+ uint16_t position{positionUnspecified};
+
+ FormatFill fill{FormatFill::make()};
- FormatType formatType;
+ FormatSign formatSign{FormatSign::Default};
- bool hasWidth;
- bool takesWidth;
+ FormatType formatType{};
- bool hasPrecision;
- bool takesPrecision;
+ bool takesWidth{false};
- bool hasFill;
+ bool takesPrecision{false};
- bool hasFormatSign;
+ bool alternateForm{false};
- bool alternateForm;
+ bool leadingZero{false};
- bool leadingZero;
+ bool hasLocale{false};
- static Result<FormatSpec, FudStatus> make(StringView& formatView, size_t specIndex);
+ static Result<FormatSpec, FudStatus> make(StringView formatView, size_t& specLength);
};
} // namespace fud
diff --git a/include/fud_option.hpp b/include/fud_option.hpp
index ca3954f..931ef82 100644
--- a/include/fud_option.hpp
+++ b/include/fud_option.hpp
@@ -103,16 +103,16 @@ class Option {
}
}
- constexpr Option(const Option& rhs) noexcept : m_engaged(rhs.m_engaged), m_data(rhs.m_data)
+ constexpr Option(const Option& rhs) noexcept : m_data(rhs.m_data), m_engaged(rhs.m_engaged)
{
}
- constexpr Option(Option&& rhs) noexcept : m_engaged(rhs.m_engaged), m_data(std::move(rhs.m_data))
+ constexpr Option(Option&& rhs) noexcept : m_data(std::move(rhs.m_data)), m_engaged(rhs.m_engaged)
{
rhs.cleanup();
}
- ~Option() noexcept
+ constexpr ~Option() noexcept
{
destroy();
}
@@ -142,6 +142,11 @@ class Option {
return m_engaged;
}
+ [[nodiscard]] bool isNone() const
+ {
+ return !m_engaged;
+ }
+
operator bool() const {
return hasValue();
}
@@ -170,7 +175,23 @@ class Option {
{
fudAssert(m_engaged);
static_assert(!IsRef);
- return *reinterpret_cast<const ValueType*>(m_data.data());
+ return std::move(*reinterpret_cast<const ValueType*>(m_data.data()));
+ }
+
+ [[nodiscard]] constexpr const ValueType& valueOr(const ValueType& alternative) const&
+ {
+ if (m_engaged) {
+ return value();
+ }
+ return alternative;
+ }
+
+ [[nodiscard]] constexpr ValueType&& valueOr(ValueType&& alternative) const&&
+ {
+ if (m_engaged) {
+ return value();
+ }
+ return std::move(alternative);
}
template <typename F>
@@ -203,27 +224,11 @@ class Option {
m_data.clear();
}
- // alignas(maxAlign) Array<uint8_t, maxSize> priv_m_data;
-
alignas(alignof(T)) option_detail::DataArray<Size> m_data{};
bool m_engaged;
};
-namespace test {
-
-void testOption()
-{
- Option<int> intOpt;
- static_cast<void>(intOpt);
- Option<int&> intRefNull;
- static_cast<void>(intRefNull);
- int value;
- Option<int&> intRefValue{value};
-}
-
-} // namespace test
-
} // namespace fud
#endif
diff --git a/include/fud_string_convert.hpp b/include/fud_string_convert.hpp
new file mode 100644
index 0000000..597c6a9
--- /dev/null
+++ b/include/fud_string_convert.hpp
@@ -0,0 +1,456 @@
+/*
+ * 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_STRING_CONVERT_HPP
+#define FUD_STRING_CONVERT_HPP
+
+#include "fud_algorithm.hpp"
+#include "fud_option.hpp"
+#include "fud_result.hpp"
+#include "fud_status.hpp"
+#include "fud_string.hpp"
+#include "fud_string_view.hpp"
+
+#include <cmath>
+#include <cstdint>
+
+namespace fud {
+
+enum class Radix : uint8_t
+{
+ Binary = 2,
+ Octal = 8,
+ Decimal = 10,
+ Hexadecimal = 16,
+};
+
+constexpr uint8_t RadixMax = 36;
+
+template <typename T>
+struct ConvertValue {
+ size_t nextIndex;
+ T value;
+};
+
+template <typename T>
+using StringConvertResult = Result<ConvertValue<T>, FudStatus>;
+
+template <typename T>
+StringConvertResult<T> fromString(StringView inputView, Option<uint8_t> specifiedRadixOption = NullOpt);
+
+template <typename T>
+StringConvertResult<T> fromString(StringView inputView, Radix specifiedRadixOption);
+
+template <typename T>
+StringConvertResult<T> fromString(const String& inputView, Option<uint8_t> specifiedRadixOption = NullOpt);
+
+namespace impl {
+constexpr Array<int8_t, 256> AsciiLookup{
+ {-1, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
+ -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ -2, -2, -2, -2, -2, -2, -2, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 33, 34, 35, -2, -2, -2, -2, -2, -2, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28,
+ 29, 30, 31, 32, 33, 34, 35, -2, -2, -2, -2, -2, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3,
+ -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3,
+ -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3,
+ -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3,
+ -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3}};
+
+// NOLINTBEGIN(readability-magic-numbers)
+static_assert(AsciiLookup[static_cast<size_t>('0')] == 0);
+static_assert(AsciiLookup[static_cast<size_t>('1')] == 1);
+static_assert(AsciiLookup[static_cast<size_t>('2')] == 2);
+static_assert(AsciiLookup[static_cast<size_t>('3')] == 3);
+static_assert(AsciiLookup[static_cast<size_t>('4')] == 4);
+static_assert(AsciiLookup[static_cast<size_t>('5')] == 5);
+static_assert(AsciiLookup[static_cast<size_t>('6')] == 6);
+static_assert(AsciiLookup[static_cast<size_t>('7')] == 7);
+static_assert(AsciiLookup[static_cast<size_t>('8')] == 8);
+static_assert(AsciiLookup[static_cast<size_t>('9')] == 9);
+static_assert(AsciiLookup[static_cast<size_t>('a')] == 10);
+static_assert(AsciiLookup[static_cast<size_t>('b')] == 11);
+static_assert(AsciiLookup[static_cast<size_t>('c')] == 12);
+static_assert(AsciiLookup[static_cast<size_t>('d')] == 13);
+static_assert(AsciiLookup[static_cast<size_t>('e')] == 14);
+static_assert(AsciiLookup[static_cast<size_t>('f')] == 15);
+static_assert(AsciiLookup[static_cast<size_t>('A')] == 10);
+static_assert(AsciiLookup[static_cast<size_t>('B')] == 11);
+static_assert(AsciiLookup[static_cast<size_t>('C')] == 12);
+static_assert(AsciiLookup[static_cast<size_t>('D')] == 13);
+static_assert(AsciiLookup[static_cast<size_t>('E')] == 14);
+static_assert(AsciiLookup[static_cast<size_t>('F')] == 15);
+static_assert(AsciiLookup[127] == -2);
+static_assert(AsciiLookup[128] == -3);
+// NOLINTEND(readability-magic-numbers)
+
+FudStatus checkPlusSigned(StringView& view, size_t& skipIndex);
+
+Result<Radix, FudStatus> determineRadix(StringView input, size_t& index);
+
+Result<bool, FudStatus> checkNegative(StringView& view, size_t& skipIndex);
+
+Result<uint8_t, FudStatus> getRadix(StringView& view, size_t& skipIndex, Option<uint8_t> specifiedRadixOption);
+
+template <typename T>
+StringConvertResult<T> unsignedFromString(StringView nextView, size_t skipIndex, Option<uint8_t> specifiedRadixOption)
+{
+ static_assert(std::is_unsigned_v<T> && std::is_integral_v<T>);
+ auto status = checkPlusSigned(nextView, skipIndex);
+ if (status != FudStatus::Success) {
+ return FudStatus::ArgumentInvalid;
+ }
+
+ auto radixResult = impl::getRadix(nextView, skipIndex, specifiedRadixOption);
+ if (radixResult.isError()) {
+ return radixResult.takeError();
+ }
+ auto radix = radixResult.takeOkay();
+
+ T num = 0;
+ size_t digitIndex = 0;
+
+ while (digitIndex < nextView.length()) {
+ auto digitResult = impl::AsciiLookup[nextView.data()[digitIndex]];
+ if (digitResult >= radix || digitResult < 0) {
+ break;
+ }
+
+ auto digit = static_cast<uint8_t>(digitResult);
+ if (std::numeric_limits<T>::max() / radix < num) {
+ return FudStatus::RangeError;
+ }
+ num *= radix;
+ if (std::numeric_limits<T>::max() - digit < num) {
+ return FudStatus::RangeError;
+ }
+ num += digit;
+ digitIndex++;
+ }
+ if (digitIndex < 1) {
+ return FudStatus::ArgumentInvalid;
+ }
+
+ return ConvertValue{skipIndex + digitIndex, num};
+}
+
+template <typename T>
+FudStatus signedPositiveFromString(StringView view, uint8_t radix, size_t& digitIndex, T& num)
+{
+ digitIndex = 0;
+ while (digitIndex < view.length()) {
+ int8_t digitResult = impl::AsciiLookup[view.data()[digitIndex]];
+ if (digitResult >= radix || digitResult < 0) {
+ break;
+ }
+
+ auto digit = static_cast<uint8_t>(digitResult);
+ if (std::numeric_limits<T>::max() / radix < num) {
+ return FudStatus::RangeError;
+ }
+ num = static_cast<T>(num * radix);
+ if (std::numeric_limits<T>::max() - digit < num) {
+ return FudStatus::RangeError;
+ }
+ num = static_cast<T>(num + digit);
+ digitIndex++;
+ }
+
+ return FudStatus::Success;
+}
+
+template <typename T>
+FudStatus signedNegativeFromString(StringView view, uint8_t radix, size_t& digitIndex, T& num)
+{
+ digitIndex = 0;
+ while (digitIndex < view.length()) {
+ int8_t digitResult = impl::AsciiLookup[view.data()[digitIndex]];
+ if (digitResult >= radix || digitResult < 0) {
+ break;
+ }
+
+ auto digit = static_cast<uint8_t>(digitResult);
+ if ((std::numeric_limits<T>::min() / radix > num)) {
+ return FudStatus::RangeError;
+ }
+ num = static_cast<T>(num * radix);
+ if (std::numeric_limits<T>::min() + digit > num) {
+ return FudStatus::RangeError;
+ }
+ num = static_cast<T>(num - digit);
+ digitIndex++;
+ }
+
+ return FudStatus::Success;
+}
+
+template <typename T>
+StringConvertResult<T> signedFromString(StringView nextView, size_t skipIndex, Option<uint8_t> specifiedRadixOption)
+{
+ static_assert(std::is_signed_v<T> && std::is_integral_v<T>);
+ auto status = impl::checkPlusSigned(nextView, skipIndex);
+ if (status != FudStatus::Success) {
+ return FudStatus::ArgumentInvalid;
+ }
+
+ auto radixResult = impl::getRadix(nextView, skipIndex, specifiedRadixOption);
+ if (radixResult.isError()) {
+ return radixResult.takeError();
+ }
+ auto radix = radixResult.takeOkay();
+
+ T num = 0;
+ size_t digitIndex = 0;
+
+ auto isNegativeResult = checkNegative(nextView, skipIndex);
+ if (isNegativeResult.isError()) {
+ return isNegativeResult.takeError();
+ }
+ const auto isNegative = isNegativeResult.takeOkay();
+
+ if (isNegative) {
+ status = signedNegativeFromString(nextView, radix, digitIndex, num);
+ } else {
+ status = signedPositiveFromString(nextView, radix, digitIndex, num);
+ }
+
+ if (status != FudStatus::Success) {
+ return status;
+ }
+
+ if (digitIndex < 1) {
+ return FudStatus::ArgumentInvalid;
+ }
+
+ return ConvertValue{skipIndex + digitIndex, num};
+}
+
+template <typename T>
+bool isNanOrInf(T& num, StringView& view, T& sign, size_t& digitIndex)
+{
+ if (view.length() >= 3) {
+ Array<uint8_t, 3> letters{{view.data()[0], view.data()[1], view.data()[2]}};
+ forEach(letters.span(), charToLower);
+ if (letters[0] == 'i' && letters[1] == 'n' && letters[2] == 'f') {
+ num = sign * std::numeric_limits<T>::infinity();
+ digitIndex = 3;
+ return true;
+ }
+ if (letters[0] == 'n' && letters[1] == 'a' && letters[2] == 'n') {
+ num = std::numeric_limits<T>::quiet_NaN();
+ digitIndex = 3;
+ return true;
+ }
+ }
+ return false;
+}
+
+template <typename T>
+FudStatus getWhole(
+ const StringView view,
+ size_t& digitIndex,
+ T& num,
+ T sign,
+ uint8_t radix,
+ bool& foundDecimal,
+ bool& foundExponent)
+{
+ while (digitIndex < view.length()) {
+ auto nextChar = view.data()[digitIndex];
+ if (nextChar == '.') {
+ foundDecimal = true;
+ digitIndex++;
+ break;
+ }
+
+ if (radix == static_cast<uint8_t>(Radix::Decimal) && (nextChar == 'e' || nextChar == 'E')) {
+ foundExponent = true;
+ digitIndex++;
+ break;
+ }
+
+ auto digitResult = impl::AsciiLookup[nextChar];
+ if (digitResult >= radix) {
+ return FudStatus::ArgumentInvalid;
+ }
+ if (digitResult < 0) {
+ break;
+ }
+ auto digit = static_cast<T>(digitResult) * sign;
+ num *= static_cast<T>(radix);
+
+ num += digit;
+ digitIndex++;
+ }
+ return FudStatus::Success;
+}
+
+template <typename T>
+FudStatus getExponent(StringView view, size_t& digitIndex, T& num, uint8_t radix)
+{
+ StringView tempView{view.length() - digitIndex, view.data() + digitIndex};
+ size_t exponentLength{};
+ auto convertResult = signedFromString<int32_t>(tempView, exponentLength, static_cast<uint8_t>(Radix::Decimal));
+ if (convertResult.isError()) {
+ return convertResult.takeError();
+ }
+ auto convertValue = convertResult.takeOkay();
+ digitIndex += convertValue.nextIndex + exponentLength;
+ num = num * std::pow(static_cast<T>(radix), static_cast<T>(convertValue.value));
+ return FudStatus::Success;
+}
+
+template <typename T>
+FudStatus getFraction(const StringView view, size_t& digitIndex, T& num, T sign, uint8_t radix, bool& foundExponent)
+{
+ auto radixDiv = static_cast<T>(1) / static_cast<T>(radix);
+ while (digitIndex < view.length()) {
+ auto nextChar = view.data()[digitIndex];
+ if (radix == static_cast<uint8_t>(Radix::Decimal) && (nextChar == 'e' || nextChar == 'E')) {
+ foundExponent = true;
+ digitIndex++;
+ break;
+ }
+
+ auto digitResult = impl::AsciiLookup[nextChar];
+ if (digitResult >= radix) {
+ return FudStatus::ArgumentInvalid;
+ }
+ if (digitResult < 0) {
+ break;
+ }
+ auto digit = static_cast<T>(digitResult) * sign;
+ num += digit * radixDiv;
+ radixDiv /= static_cast<T>(radix);
+ digitIndex++;
+ }
+ return FudStatus::Success;
+}
+
+template <typename T>
+StringConvertResult<T> floatFromString(StringView nextView, size_t skipIndex, Option<uint8_t> specifiedRadixOption)
+{
+ static_assert(std::is_floating_point_v<T>);
+ if (nextView.length() < 1) {
+ return FudStatus::ArgumentInvalid;
+ }
+
+ auto isNegativeResult = checkNegative(nextView, skipIndex);
+ if (isNegativeResult.isError()) {
+ return isNegativeResult.takeError();
+ }
+ const auto isNegative = isNegativeResult.takeOkay();
+
+ if (!isNegative) {
+ auto status = checkPlusSigned(nextView, skipIndex);
+ if (status != FudStatus::Success) {
+ return FudStatus::ArgumentInvalid;
+ }
+ }
+ T sign = isNegative ? -1.0 : 1.0;
+
+ T num = 0;
+ size_t digitIndex = 0;
+
+ auto retSuccess = [&]() { return ConvertValue{skipIndex + digitIndex, num}; };
+
+ if (impl::isNanOrInf(num, nextView, sign, digitIndex)) {
+ return retSuccess();
+ }
+
+ auto radixResult = impl::getRadix(nextView, skipIndex, specifiedRadixOption);
+ if (radixResult.isError()) {
+ return radixResult.takeError();
+ }
+ auto radix = radixResult.takeOkay();
+
+ bool foundDecimal = false;
+ bool foundExponent = false;
+ auto status = getWhole(nextView, digitIndex, num, sign, radix, foundDecimal, foundExponent);
+
+ if (status == FudStatus::Success && foundExponent) {
+ status = getExponent(nextView, digitIndex, num, radix);
+ }
+
+ if (status != FudStatus::Success) {
+ return status;
+ }
+
+ if (!foundDecimal) {
+ if (digitIndex < 1) {
+ return FudStatus::ArgumentInvalid;
+ }
+
+ return retSuccess();
+ }
+
+ status = getFraction(nextView, digitIndex, num, sign, radix, foundExponent);
+
+ if (foundExponent) {
+ status = getExponent(nextView, digitIndex, num, radix);
+ if (status != FudStatus::Success) {
+ return status;
+ }
+ }
+
+ if (digitIndex < 1) {
+ return FudStatus::ArgumentInvalid;
+ }
+
+ if (std::isinf(num) || std::isnan(num)) // isnan is dubious here - likely unreachable
+ {
+ return FudStatus::RangeError;
+ }
+
+ return retSuccess();
+}
+
+} // namespace impl
+
+template <typename T>
+StringConvertResult<T> fromString(StringView inputView, Radix specifiedRadixOption)
+{
+ return fromString<T>(inputView, static_cast<uint8_t>(specifiedRadixOption));
+}
+
+template <typename T>
+StringConvertResult<T> fromString(StringView inputView, Option<uint8_t> specifiedRadixOption)
+{
+ if (inputView.data() == nullptr) {
+ return FudStatus::NullPointer;
+ }
+
+ StringView nextView{inputView};
+ auto skipResult = nextView.skipWhitespace();
+ if (skipResult.isError()) {
+ return skipResult.takeError();
+ }
+ size_t skipIndex = skipResult.takeOkay();
+
+ if constexpr (std::is_unsigned_v<T> && std::is_integral_v<T>) {
+ return impl::unsignedFromString<T>(nextView, skipIndex, specifiedRadixOption);
+ } else if constexpr (std::is_signed_v<T> && std::is_integral_v<T>) {
+ return impl::signedFromString<T>(nextView, skipIndex, specifiedRadixOption);
+ } else if constexpr (std::is_floating_point_v<T>) {
+ return impl::floatFromString<T>(nextView, skipIndex, specifiedRadixOption);
+ } else {
+ return FudStatus::NotImplemented;
+ }
+}
+
+} // namespace fud
+
+#endif
diff --git a/include/fud_string_view.hpp b/include/fud_string_view.hpp
index 8a47ae5..0852645 100644
--- a/include/fud_string_view.hpp
+++ b/include/fud_string_view.hpp
@@ -20,6 +20,8 @@
#include "fud_status.hpp"
#include "fud_utf8.hpp"
+#include "fud_config.hpp"
+#include "fud_assert.hpp"
#include <string_view>
@@ -63,6 +65,25 @@ struct StringView {
return m_data;
}
+ constexpr const utf8& operator[](size_t index) const
+ {
+ if constexpr (fudBoundsChecking) {
+ fudAssert(m_data != nullptr);
+ fudAssert(index < m_length);
+ }
+ return m_data[index];
+ }
+
+ constexpr const utf8* begin() const noexcept
+ {
+ return m_data;
+ }
+
+ constexpr const utf8* end() const noexcept
+ {
+ return m_data + m_length;
+ }
+
[[nodiscard]] bool nullTerminated() const;
[[nodiscard]] bool utf8Valid() const;
@@ -79,26 +100,6 @@ struct StringView {
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;
-
- FudStatus toUint32(uint32_t& number, uint8_t specifiedRadix, size_t& strLen) const;
-
- FudStatus toUint64(uint64_t& number, uint8_t specifiedRadix, size_t& strLen) const;
-
- FudStatus toInt8(int8_t& number, uint8_t specifiedRadix, size_t& strLen) const;
-
- FudStatus toInt16(int16_t& number, uint8_t specifiedRadix, size_t& strLen) const;
-
- FudStatus toInt32(int32_t& number, uint8_t specifiedRadix, size_t& strLen) const;
-
- FudStatus toInt64(int64_t& number, uint8_t specifiedRadix, size_t& strLen) const;
-
- FudStatus toFloat(float& number, size_t& strLen) const;
-
- FudStatus toDouble(double& number, size_t& strLen) const;
-
size_t m_length{0};
const utf8* m_data{nullptr};
diff --git a/include/fud_utf8.hpp b/include/fud_utf8.hpp
index 3b1a6b7..50e50aa 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_unique_array.hpp"
#include "fud_c_string.hpp"
+#include "fud_unique_array.hpp"
#include <cstdint>
#include <optional>
@@ -28,8 +28,6 @@
namespace fud {
-
-
using utf8 = unsigned char;
class String;
@@ -113,8 +111,7 @@ struct Utf82Byte {
{
}
- __attribute__((nonnull))
- constexpr Utf82Byte(const char* letterStr) noexcept : characters{}
+ __attribute__((nonnull)) constexpr Utf82Byte(const char* letterStr) noexcept : characters{}
{
auto length = cStringLength(letterStr, 2);
if (length < 2) {
@@ -159,8 +156,7 @@ struct Utf83Byte {
{
}
- __attribute__((nonnull))
- constexpr Utf83Byte(const char* letterStr) noexcept : characters{}
+ __attribute__((nonnull)) constexpr Utf83Byte(const char* letterStr) noexcept : characters{}
{
auto length = cStringLength(letterStr, 3);
if (length < 3) {
@@ -213,8 +209,7 @@ struct Utf84Byte {
{
}
- __attribute__((nonnull))
- constexpr Utf84Byte(const char* letterStr) noexcept : characters{}
+ __attribute__((nonnull)) constexpr Utf84Byte(const char* letterStr) noexcept : characters{}
{
auto length = cStringLength(letterStr, 4);
if (length < 4) {
@@ -327,7 +322,8 @@ struct FudUtf8 {
return unicode;
}
- static constexpr FudUtf8 make(Utf8Variant utf8Variant) {
+ static constexpr FudUtf8 make(Utf8Variant utf8Variant)
+ {
FudUtf8 unicode{};
unicode.m_variant = utf8Variant;
if (!std::visit([](auto arg) { return arg.valid(); }, utf8Variant)) {
@@ -513,82 +509,128 @@ struct FudUtf8 {
}
};
+namespace classify {
+
+using CharPredicate = bool (*)(char);
+using Utf8Predicate = bool (*)(utf8);
+using FudUtf8Predicate = bool (*)(FudUtf8);
+
/** \brief Checks if a character is ascii. */
-[[nodiscard]] bool charIsAscii(char character);
+[[nodiscard]] bool isAscii(char character);
-[[nodiscard]] bool utf8IsAscii(FudUtf8 character);
+[[nodiscard]] bool isAscii(utf8 character);
+
+[[nodiscard]] bool isAscii(FudUtf8 character);
/** \brief Checks if a character is alphanumeric. */
-[[nodiscard]] bool charIsAlphanumeric(char character);
+[[nodiscard]] bool isAlphanumeric(char character);
/** \brief Checks if a character is alphanumeric. */
-[[nodiscard]] bool utf8IsAlphanumeric(FudUtf8 character);
+[[nodiscard]] bool isAlphanumeric(utf8 character);
+
+/** \brief Checks if a character is alphanumeric. */
+[[nodiscard]] bool isAlphanumeric(FudUtf8 character);
+
+/** \brief Checks if a character is alphabetic. */
+[[nodiscard]] bool isAlpha(char character);
/** \brief Checks if a character is alphabetic. */
-[[nodiscard]] bool charIsAlpha(char character);
+[[nodiscard]] bool isAlpha(utf8 character);
/** \brief Checks if a character is alphabetic. */
-[[nodiscard]] bool utf8IsAlpha(FudUtf8 character);
+[[nodiscard]] bool isAlpha(FudUtf8 character);
+
+/** \brief Checks if a character is lowercase. */
+[[nodiscard]] bool isLowercase(char character);
/** \brief Checks if a character is lowercase. */
-[[nodiscard]] bool charIsLowercase(char character);
+[[nodiscard]] bool isLowercase(utf8 character);
/** \brief Checks if a character is lowercase. */
-[[nodiscard]] bool utf8IsLowercase(FudUtf8 character);
+[[nodiscard]] bool isLowercase(FudUtf8 character);
+
+/** \brief Checks if a character is uppercase. */
+[[nodiscard]] bool isUppercase(char character);
-/** \brief Checks if a character is an uppercase character. */
-[[nodiscard]] bool charIsUppercase(char character);
+/** \brief Checks if a character is uppercase. */
+[[nodiscard]] bool isUppercase(utf8 character);
/** \brief Checks if a character is uppercase. */
-[[nodiscard]] bool utf8IsUppercase(FudUtf8 character);
+[[nodiscard]] bool isUppercase(FudUtf8 character);
/** \brief Checks if a character is a digit. */
-[[nodiscard]] bool charIsDigit(char character);
+[[nodiscard]] bool isDigit(char character);
/** \brief Checks if a character is a digit. */
-[[nodiscard]] bool utf8IsDigit(FudUtf8 character);
+[[nodiscard]] bool isDigit(utf8 character);
+
+/** \brief Checks if a character is a digit. */
+[[nodiscard]] bool isDigit(FudUtf8 character);
/** \brief Checks if a character is a hexadecimal character. */
-[[nodiscard]] bool charIsHexDigit(char character);
+[[nodiscard]] bool isHexDigit(char character);
+
+/** \brief Checks if a character is a hexadecimal character. */
+[[nodiscard]] bool isHexDigit(utf8 character);
/** \brief Checks if a character is a hexadecimal digit. */
-[[nodiscard]] bool utf8IsHexDigit(FudUtf8 character);
+[[nodiscard]] bool isHexDigit(FudUtf8 character);
/** \brief Checks if a character is a control character. */
-[[nodiscard]] bool charIsControl(char character);
+[[nodiscard]] bool isControl(char character);
/** \brief Checks if a character is a control character. */
-[[nodiscard]] bool utf8IsControl(FudUtf8 character);
+[[nodiscard]] bool isControl(utf8 character);
+
+/** \brief Checks if a character is a control character. */
+[[nodiscard]] bool isControl(FudUtf8 character);
+
+/** \brief Checks if a character is a graphical character. */
+[[nodiscard]] bool isGraphical(char character);
/** \brief Checks if a character is a graphical character. */
-[[nodiscard]] bool charIsGraphical(char character);
+[[nodiscard]] bool isGraphical(utf8 character);
/** \brief Checks if a character is a graphical character. */
-[[nodiscard]] bool utf8IsGraphical(FudUtf8 character);
+[[nodiscard]] bool isGraphical(FudUtf8 character);
/** \brief Checks if a character is a space character. */
-[[nodiscard]] bool charIsSpace(char character);
+[[nodiscard]] bool isSpace(char character);
/** \brief Checks if a character is a space character. */
-[[nodiscard]] bool utf8IsSpace(FudUtf8 character);
+[[nodiscard]] bool isSpace(utf8 character);
+
+/** \brief Checks if a character is a space character. */
+[[nodiscard]] bool isSpace(FudUtf8 character);
+
+/** \brief Checks if a character is a blank character. */
+[[nodiscard]] bool isBlank(char character);
/** \brief Checks if a character is a blank character. */
-[[nodiscard]] bool charIsBlank(char character);
+[[nodiscard]] bool isBlank(utf8 character);
/** \brief Checks if a character is a blank character. */
-[[nodiscard]] bool utf8IsBlank(FudUtf8 character);
+[[nodiscard]] bool isBlank(FudUtf8 character);
+
+/** \brief Checks if a character is a printable character. */
+[[nodiscard]] bool isPrintable(char character);
/** \brief Checks if a character is a printable character. */
-[[nodiscard]] bool charIsPrintable(char character);
+[[nodiscard]] bool isPrintable(utf8 character);
/** \brief Checks if a character is a printable character. */
-[[nodiscard]] bool utf8IsPrintable(FudUtf8 character);
+[[nodiscard]] bool isPrintable(FudUtf8 character);
+
+/** \brief Checks if a character is a punctuation character. */
+[[nodiscard]] bool isPunctuation(char character);
/** \brief Checks if a character is a punctuation character. */
-[[nodiscard]] bool charIsPunctuation(char character);
+[[nodiscard]] bool isPunctuation(utf8 character);
/** \brief Checks if a character is a punctuation character. */
-[[nodiscard]] bool utf8IsPunctuation(FudUtf8 character);
+[[nodiscard]] bool isPunctuation(FudUtf8 character);
+
+} // namespace classify
/** \brief Converts character to lowercase if valid. */
uint8_t charToLower(uint8_t character);
diff --git a/source/fud_assert.cpp b/source/fud_assert.cpp
index 3df6734..ccc90a5 100644
--- a/source/fud_assert.cpp
+++ b/source/fud_assert.cpp
@@ -1,6 +1,7 @@
#include "fud_assert.hpp"
// #include "fud_array.hpp"
+// #include "fud_format.hpp"
#include <climits>
#include <cstdio>
diff --git a/source/fud_format.cpp b/source/fud_format.cpp
index ab1bb4f..04e611e 100644
--- a/source/fud_format.cpp
+++ b/source/fud_format.cpp
@@ -1,14 +1,515 @@
-// #include "fud_format.hpp"
+#include "fud_format.hpp"
+
+#include "fud_string_convert.hpp"
+#include "fud_utf8.hpp"
namespace fud {
-/*
-Result<FormatSpec, FudStatus> FormatSpec::make(StringView view, size_t& length)
+namespace impl {
+
+Result<size_t, FudStatus> preScanSpec(StringView formatView, FormatSpec& spec, bool& hasPosition, bool& hasColon);
+
+FudStatus getPosition(StringView& formatView, FormatSpec& spec, bool& hasPosition);
+
+FudStatus getFill(StringView& formatView, FormatSpec& spec);
+
+FudStatus getSpecialOpts(StringView& formatView, FormatSpec& spec);
+
+FudStatus getWidth(StringView& formatView, FormatSpec& spec);
+
+FudStatus getPrecision(StringView& formatView, FormatSpec& spec);
+
+FudStatus getFormatSign(StringView& formatView, FormatSpec& spec);
+
+} // namespace impl
+
+Result<FormatSpec, FudStatus> FormatSpec::make(StringView formatView, size_t& specLength)
+{
+ static_cast<void>(formatView);
+ static_cast<void>(specLength);
+
+ if (formatView[0] != FormatSpec::openBracket) {
+ return FudStatus::ArgumentInvalid;
+ }
+
+ auto hasEnded = [](StringView& view) {
+ fudAssert(view.length() > 0);
+ return view[0] == FormatSpec::closeBracket;
+ };
+
+ FormatSpec spec{};
+ size_t length = 1;
+
+ formatView.advanceUnsafe();
+
+ bool hasPosition{false};
+ bool hasColon{false};
+ auto lengthResult = impl::preScanSpec(formatView, spec, hasPosition, hasColon);
+ if (lengthResult.isError()) {
+ return lengthResult.takeError();
+ }
+
+ length += lengthResult.takeOkay();
+ if (length < 1) {
+ return FudStatus::FormatInvalid;
+ }
+ specLength = length;
+
+ auto status = impl::getPosition(formatView, spec, hasPosition);
+ if (status != FudStatus::Success) {
+ return status;
+ }
+
+ // check early ending
+
+ if (!hasColon) {
+ if (hasEnded(formatView)) {
+ return spec;
+ }
+ return FudStatus::FormatInvalid;
+ }
+
+ // check validity for being non-default spec and advance
+
+ if (formatView[0] != ':') {
+ return FudStatus::FormatInvalid;
+ }
+
+ formatView.advanceUnsafe();
+ if (hasEnded(formatView)) {
+ return spec;
+ }
+
+ // Spec
+
+ status = impl::getFill(formatView, spec);
+ if (status != FudStatus::Success) {
+ return status;
+ }
+
+ if (hasEnded(formatView)) {
+ return spec;
+ }
+
+ // sign, alternate, leading zero
+
+ status = impl::getSpecialOpts(formatView, spec);
+ if (status != FudStatus::Success) {
+ return status;
+ }
+
+ if (hasEnded(formatView)) {
+ return spec;
+ }
+
+ // Width
+
+ status = impl::getWidth(formatView, spec);
+ if (status != FudStatus::Success) {
+ return status;
+ }
+
+ if (hasEnded(formatView)) {
+ return spec;
+ }
+
+ // Precision
+
+ status = impl::getPrecision(formatView, spec);
+ if (status != FudStatus::Success) {
+ return status;
+ }
+
+ if (hasEnded(formatView)) {
+ return spec;
+ }
+
+ // Locale
+
+ if (formatView[0] == FormatSpec::localeChar) {
+ spec.hasLocale = true;
+ formatView.advanceUnsafe();
+ }
+
+ if (hasEnded(formatView)) {
+ return spec;
+ }
+
+ // Format Sign
+ status = impl::getFormatSign(formatView, spec);
+ if (status != FudStatus::Success) {
+ return status;
+ }
+
+ if (!hasEnded(formatView)) {
+ return FudStatus::FormatInvalid;
+ }
+
+ return spec;
+}
+
+namespace impl {
+
+Result<size_t, FudStatus> preScanSpec(StringView formatView, FormatSpec& spec, bool& hasPosition, bool& hasColon)
+{
+ int nesting = 0;
+ uint32_t captureGroups = 0;
+ size_t index = 0;
+ hasColon = false;
+ while (index < formatView.length() && (formatView[index] != FormatSpec::closeBracket || nesting > 0)) {
+ auto letter = formatView[index];
+ bool isDigit = classify::isDigit(static_cast<char>(letter));
+ bool priorDot = index > 0 && formatView[index - 1] == '.';
+ bool takesWidth = not spec.takesWidth and not priorDot;
+ bool takesPrecision = not spec.takesPrecision and priorDot;
+ switch (letter) {
+ case FormatSpec::openBracket:
+ if (not hasColon) {
+ return FudStatus::FormatInvalid;
+ }
+ if (takesWidth) {
+ spec.takesWidth = true;
+ } else if (takesPrecision) {
+ spec.takesPrecision = true;
+ } else {
+ return FudStatus::FormatInvalid;
+ }
+ nesting++;
+ captureGroups++;
+ break;
+ case FormatSpec::closeBracket:
+ nesting--;
+ break;
+ case ':':
+ hasColon = true;
+ break;
+ case '.':
+ break;
+ case 'L':
+ spec.hasLocale = true;
+ break;
+ default:
+ if (not hasColon and isDigit) {
+ hasPosition = true;
+ }
+ break;
+ }
+
+ if (nesting > 1) {
+ return FudStatus::FormatInvalid;
+ }
+
+ index++;
+ }
+
+ if (nesting != 0 || captureGroups > 2 || index >= formatView.length() ||
+ formatView[index] != FormatSpec::closeBracket) {
+ return FudStatus::FormatInvalid;
+ }
+
+ return index + 1U;
+}
+
+FudStatus getPosition(StringView& formatView, FormatSpec& spec, bool& hasPosition)
{
- static_cast<void>(view);
- static_cast<void>(length);
- return FudStatus::NotImplemented;
+ if (hasPosition) {
+ auto positionResult{fromString<decltype(spec.position)>(formatView, Radix::Decimal)};
+ if (positionResult.isError()) {
+ return positionResult.takeError();
+ }
+ auto positionValue{positionResult.takeOkay()};
+ spec.position = positionValue.value;
+ auto advanceStatus = formatView.advance(positionValue.nextIndex);
+ if (!advanceStatus) {
+ return FudStatus::Failure;
+ }
+ }
+
+ return FudStatus::Success;
}
-*/
+
+FudStatus getFill(StringView& formatView, FormatSpec& spec)
+{
+ Option<FormatFill> fillOpt = NullOpt;
+ if (formatView[0] != FormatSpec::openBracket) {
+ size_t fillLength = 0;
+ auto fillResult = FormatFill::make(formatView, fillLength);
+ if (fillResult.isError()) {
+ return fillResult.takeError();
+ }
+ fillOpt = fillResult.takeOkay();
+ formatView.advanceUnsafe(fillLength);
+ }
+ spec.fill = fillOpt.valueOr(FormatFill::make());
+ return FudStatus::Success;
+}
+
+FudStatus getSpecialOpts(StringView& formatView, FormatSpec& spec)
+{
+ if (formatView.length() < 1) {
+ return FudStatus::ArgumentInvalid;
+ }
+
+ bool done = false;
+ switch (formatView[0]) {
+ case static_cast<utf8>(FormatSign::Plus):
+ spec.formatSign = FormatSign::Plus;
+ break;
+ case static_cast<utf8>(FormatSign::Minus):
+ spec.formatSign = FormatSign::Minus;
+ break;
+ case static_cast<utf8>(FormatSign::Space):
+ spec.formatSign = FormatSign::Space;
+ break;
+ case '#':
+ spec.alternateForm = true;
+ break;
+ case '0':
+ spec.leadingZero = true;
+ done = true;
+ break;
+ default:
+ return FudStatus::Success;
+ }
+
+ formatView.advanceUnsafe();
+ if (done || formatView.length() < 1) {
+ return FudStatus::Success;
+ }
+
+ switch (formatView[0]) {
+ case '#':
+ if (spec.alternateForm) {
+ return FudStatus::FormatInvalid;
+ }
+ spec.alternateForm = true;
+ break;
+ case '0':
+ spec.leadingZero = true;
+ done = true;
+ break;
+ default:
+ return FudStatus::Success;
+ }
+
+ formatView.advanceUnsafe();
+ if (done || formatView.length() < 1) {
+ return FudStatus::Success;
+ }
+
+ if (formatView[0] == '0') {
+ spec.leadingZero = true;
+ formatView.advanceUnsafe();
+ }
+
+ return FudStatus::Success;
+}
+
+FudStatus getWidthTakesWidth(StringView& formatView, FormatSpec& spec);
+
+FudStatus getWidth(StringView& formatView, FormatSpec& spec)
+{
+ if (spec.takesWidth) {
+ return getWidthTakesWidth(formatView, spec);
+ }
+
+ if (formatView.length() < 1 || not classify::isDigit(formatView[0])) {
+ return FudStatus::Success;
+ }
+
+ auto widthResult{fromString<decltype(spec.width)>(formatView, Radix::Decimal)};
+ if (widthResult.isError()) {
+ return widthResult.takeError();
+ }
+ auto widthValue{widthResult.takeOkay()};
+ if (widthValue.value < 1) {
+ return FudStatus::FormatInvalid;
+ }
+ spec.width = widthValue.value;
+ if (formatView.length() < widthValue.nextIndex) {
+ return FudStatus::Failure;
+ }
+ formatView.advanceUnsafe(widthValue.nextIndex);
+
+ return FudStatus::Success;
+}
+
+FudStatus getWidthTakesWidth(StringView& formatView, FormatSpec& spec)
+{
+ if (!spec.takesWidth) {
+ return FudStatus::OperationInvalid;
+ }
+
+ if (formatView.length() < 2 || formatView[0] != FormatSpec::openBracket) {
+ return FudStatus::ArgumentInvalid;
+ }
+ formatView.advanceUnsafe();
+ if (formatView.length() > 0 && formatView[0] == FormatSpec::closeBracket &&
+ spec.position == FormatSpec::positionUnspecified) {
+ formatView.advanceUnsafe();
+ return FudStatus::Success;
+ }
+
+ if (spec.position == FormatSpec::positionUnspecified) {
+ return FudStatus::FormatInvalid;
+ }
+
+ auto positionResult{fromString<decltype(spec.position)>(formatView, Radix::Decimal)};
+ if (positionResult.isError()) {
+ return positionResult.takeError();
+ }
+ auto positionValue{positionResult.takeOkay()};
+ spec.width = positionValue.value;
+ if (formatView.length() + 1 < positionValue.nextIndex) {
+ return FudStatus::Failure;
+ }
+ if (formatView[positionValue.nextIndex + 1] != FormatSpec::closeBracket) {
+ return FudStatus::FormatInvalid;
+ }
+ formatView.advanceUnsafe(positionValue.nextIndex + 1);
+ return FudStatus::Success;
+}
+
+FudStatus getPrecisionTakesPrecision(StringView& formatView, FormatSpec& spec);
+
+FudStatus getPrecision(StringView& formatView, FormatSpec& spec)
+{
+ if (formatView.length() < 1 || formatView[0] != '.') {
+ return FudStatus::Success;
+ }
+
+ formatView.advanceUnsafe();
+
+ if (spec.takesPrecision) {
+ return getPrecisionTakesPrecision(formatView, spec);
+ }
+
+ if (formatView.length() < 1 || not classify::isDigit(formatView[0])) {
+ return FudStatus::Success;
+ }
+
+ auto precisionResult{fromString<decltype(spec.width)>(formatView, Radix::Decimal)};
+ if (precisionResult.isError()) {
+ return precisionResult.takeError();
+ }
+ auto precisionValue{precisionResult.takeOkay()};
+ if (precisionValue.value < 1) {
+ return FudStatus::FormatInvalid;
+ }
+ spec.precision = precisionValue.value;
+ if (formatView.length() < precisionValue.nextIndex) {
+ return FudStatus::Failure;
+ }
+ formatView.advanceUnsafe(precisionValue.nextIndex);
+
+ return FudStatus::Success;
+}
+
+FudStatus getPrecisionTakesPrecision(StringView& formatView, FormatSpec& spec)
+{
+ if (!spec.takesPrecision) {
+ return FudStatus::OperationInvalid;
+ }
+
+ if (formatView.length() < 2 || formatView[0] != FormatSpec::openBracket) {
+ return FudStatus::ArgumentInvalid;
+ }
+ formatView.advanceUnsafe();
+ if (formatView.length() > 0 && formatView[0] == FormatSpec::closeBracket &&
+ spec.position == FormatSpec::positionUnspecified) {
+ formatView.advanceUnsafe();
+ return FudStatus::Success;
+ }
+
+ if (spec.position == FormatSpec::positionUnspecified) {
+ return FudStatus::FormatInvalid;
+ }
+
+ auto positionResult{fromString<decltype(spec.position)>(formatView, Radix::Decimal)};
+ if (positionResult.isError()) {
+ return positionResult.takeError();
+ }
+ auto positionValue{positionResult.takeOkay()};
+ spec.precision = positionValue.value;
+ if (formatView.length() + 1 < positionValue.nextIndex) {
+ return FudStatus::Failure;
+ }
+ if (formatView[positionValue.nextIndex + 1] != FormatSpec::closeBracket) {
+ return FudStatus::FormatInvalid;
+ }
+ formatView.advanceUnsafe(positionValue.nextIndex + 1);
+ return FudStatus::Success;
+}
+
+FudStatus getFormatSign(StringView& formatView, FormatSpec& spec)
+{
+ if (formatView.length() < 1) {
+ spec.formatType = FormatType::Unspecified;
+ return FudStatus::Success;
+ }
+
+ auto letter = formatView[0];
+ switch (letter) {
+ case static_cast<utf8>(FormatType::String):
+ spec.formatType = FormatType::String;
+ break;
+ case static_cast<utf8>(FormatType::Escaped):
+ spec.formatType = FormatType::Escaped;
+ break;
+ case static_cast<utf8>(FormatType::BinaryLower):
+ spec.formatType = FormatType::BinaryLower;
+ break;
+ case static_cast<utf8>(FormatType::BinaryUpper):
+ spec.formatType = FormatType::BinaryUpper;
+ break;
+ case static_cast<utf8>(FormatType::Character):
+ spec.formatType = FormatType::Character;
+ break;
+ case static_cast<utf8>(FormatType::Decimal):
+ spec.formatType = FormatType::Decimal;
+ break;
+ case static_cast<utf8>(FormatType::Octal):
+ spec.formatType = FormatType::Octal;
+ break;
+ case static_cast<utf8>(FormatType::HexLower):
+ spec.formatType = FormatType::HexLower;
+ break;
+ case static_cast<utf8>(FormatType::HexUpper):
+ spec.formatType = FormatType::HexUpper;
+ break;
+ case static_cast<utf8>(FormatType::FloatHexLower):
+ spec.formatType = FormatType::FloatHexLower;
+ break;
+ case static_cast<utf8>(FormatType::FloatHexUpper):
+ spec.formatType = FormatType::FloatHexUpper;
+ break;
+ case static_cast<utf8>(FormatType::ScientificLower):
+ spec.formatType = FormatType::ScientificLower;
+ break;
+ case static_cast<utf8>(FormatType::ScientificUpper):
+ spec.formatType = FormatType::ScientificUpper;
+ break;
+ case static_cast<utf8>(FormatType::FixedLower):
+ spec.formatType = FormatType::FixedLower;
+ break;
+ case static_cast<utf8>(FormatType::FixedUpper):
+ spec.formatType = FormatType::FixedUpper;
+ break;
+ case static_cast<utf8>(FormatType::GeneralLower):
+ spec.formatType = FormatType::GeneralLower;
+ break;
+ case static_cast<utf8>(FormatType::GeneralUpper):
+ spec.formatType = FormatType::GeneralUpper;
+ break;
+ case static_cast<utf8>(FormatType::Unspecified):
+ default:
+ return FudStatus::FormatInvalid;
+ }
+
+ formatView.advanceUnsafe();
+ return FudStatus::Success;
+}
+
+} // namespace impl
} // namespace fud
diff --git a/source/fud_string.cpp b/source/fud_string.cpp
index b714dfc..6e16741 100644
--- a/source/fud_string.cpp
+++ b/source/fud_string.cpp
@@ -1,5 +1,5 @@
/*
- * LibFud
+ * libfud
* Copyright 2024 Dominick Allen
*
* Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/source/fud_string_convert.cpp b/source/fud_string_convert.cpp
new file mode 100644
index 0000000..428ab36
--- /dev/null
+++ b/source/fud_string_convert.cpp
@@ -0,0 +1,110 @@
+/*
+ * libfud
+ * Copyright 2024 Dominick Allen
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "fud_string_convert.hpp"
+
+namespace fud::impl {
+
+FudStatus checkPlusSigned(StringView& view, size_t& skipIndex)
+{
+ auto isPlusSigned = view.data()[0] == '+';
+ if (isPlusSigned && view.length() == 1) {
+ return FudStatus::ArgumentInvalid;
+ }
+ if (isPlusSigned) {
+ view.advanceUnsafe();
+ skipIndex++;
+ }
+ return FudStatus::Success;
+}
+
+Result<bool, FudStatus> checkNegative(StringView& view, size_t& skipIndex)
+{
+ bool isNegative = view.data()[0] == '-';
+ if (isNegative && view.length() == 1) {
+ return FudStatus::ArgumentInvalid;
+ }
+ if (isNegative) {
+ skipIndex += 1;
+ view.advanceUnsafe();
+ }
+ return isNegative;
+}
+
+Result<Radix, FudStatus> determineRadix(StringView input, size_t& index)
+{
+ if (input.length() < 1) {
+ return FudStatus::ArgumentInvalid;
+ }
+
+ if (input.length() == 1 && input.data()[0] == '0') {
+ return Radix::Octal;
+ }
+
+ if (input.length() == 1) {
+ return Radix::Decimal;
+ }
+
+ if (input.data()[0] == '0' && (input.data()[1] == 'x' || input.data()[1] == 'X')) {
+ index += 2;
+ return Radix::Hexadecimal;
+ }
+
+ if (input.data()[0] == '0') {
+ auto nextChar = input.data()[1];
+ auto nextVal = AsciiLookup[nextChar];
+ if (nextVal >= 0 && nextVal < static_cast<uint8_t>(Radix::Octal)) {
+ return Radix::Octal;
+ }
+ if (nextVal >= static_cast<uint8_t>(Radix::Octal)) {
+ return FudStatus::ArgumentInvalid;
+ }
+ }
+
+ return Radix::Decimal;
+}
+
+Result<uint8_t, FudStatus> getRadix(StringView& view, size_t& skipIndex, Option<uint8_t> specifiedRadixOption)
+{
+ if (specifiedRadixOption.isNone()) {
+ size_t radixIndex = 0;
+ auto status = determineRadix(view, radixIndex);
+ if (status.isOkay()) {
+ skipIndex += radixIndex;
+ view.advanceUnsafe(radixIndex);
+ return static_cast<uint8_t>(status.takeOkay());
+ }
+ return status.takeError();
+ }
+
+ auto radix = specifiedRadixOption.value();
+ if (radix == static_cast<uint8_t>(Radix::Hexadecimal) && view.length() > 2 &&
+ (view.data()[1] == 'x' || view.data()[1] == 'X')) {
+ skipIndex += 2;
+ view.advanceUnsafe(2);
+ } else if (radix == static_cast<uint8_t>(Radix::Binary) && view.length() > 2 && view.data()[1] == 'b') {
+ skipIndex += 2;
+ view.advanceUnsafe(2);
+ } else if (radix == static_cast<uint8_t>(Radix::Octal) && view.length() > 2 && view.data()[1] == '0') {
+ skipIndex += 2;
+ view.advanceUnsafe(2);
+ }
+
+ return radix;
+}
+
+} // namespace fud::impl
diff --git a/source/fud_string_view.cpp b/source/fud_string_view.cpp
index 1cc73a6..090dd6d 100644
--- a/source/fud_string_view.cpp
+++ b/source/fud_string_view.cpp
@@ -1,5 +1,5 @@
/*
- * LibFud
+ * libfud
* Copyright 2024 Dominick Allen
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -61,7 +61,7 @@ Result<size_t, FudStatus> StringView::skipWhitespace()
return RetType::error(FudStatus::NullPointer);
}
size_t index = 0;
- while (m_length > 0 && charIsSpace(static_cast<char>(m_data[0]))) {
+ while (m_length > 0 && classify::isSpace(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 && charIsSpace(static_cast<char>(m_data[m_length - 1]))) {
+ while (m_length > 0 && classify::isSpace(static_cast<char>(m_data[m_length - 1]))) {
m_length--;
count++;
}
@@ -120,6 +120,19 @@ void StringView::advanceUnsafe(size_t size)
m_data += size;
}
+FudStatus skipWhitespace(StringView& view, size_t& skipIndex)
+{
+ auto skipResult = view.skipWhitespace();
+ if (skipResult.isError()) {
+ return skipResult.getError();
+ }
+ skipIndex = skipResult.getOkay();
+ if (view.length() < 1) {
+ return FudStatus::ArgumentInvalid;
+ }
+ return FudStatus::Success;
+}
+
#if 0
FudStatus fud_string_truncate(ExtBasicString* source, ssize_t newLength)
@@ -134,7 +147,7 @@ FudStatus fud_string_truncate(ExtBasicString* source, ssize_t newLength)
if ((newLength > 0 && static_cast<size_t>(newLength) > source->m_length) ||
(static_cast<size_t>(-newLength) > source->m_length)) {
- return FudStatus::InvalidInput;
+ return FudStatus::ArgumentInvalid;
}
if (newLength < 0) {
@@ -158,10 +171,10 @@ FudStatus fud_string_reverse_substring(ExtBasicString* source, StringView subStr
{
auto dataOffset = subString.data - source->m_data;
if (dataOffset < 0 || static_cast<size_t>(dataOffset) > source->m_length) {
- return FudStatus::InvalidInput;
+ return FudStatus::ArgumentInvalid;
}
if (static_cast<size_t>(dataOffset) + subString.length > source->m_length) {
- return FudStatus::InvalidInput;
+ return FudStatus::ArgumentInvalid;
}
if (source == nullptr || source->m_data == nullptr) {
@@ -476,682 +489,6 @@ FudStatus fud_string_find_substring(StringView haystack, StringView needle, Stri
return ExtNotFound;
}
-FudStatus skipWhitespace(StringView& view, size_t& skipIndex)
-{
- auto skipResult = view.skipWhitespace();
- if (skipResult.isError()) {
- return skipResult.getError();
- }
- skipIndex = skipResult.getOkay();
- if (view.length < 1) {
- return FudStatus::InvalidInput;
- }
- return FudStatus::Success;
-}
-
-FudStatus fud_string_view_skip_whitespace(StringView* view)
-{
- if (view == nullptr) {
- return FudStatus::NullPointer;
- }
-
- StringView sView{*view};
- auto skipResult = sView.skipWhitespace();
- if (skipResult.isError()) {
- return skipResult.getError();
- }
- view->data = sView.data;
- view->length = sView.length;
- return FudStatus::Success;
-}
-
-FudStatus fud_string_view_trim_whitespace(StringView* view)
-{
- if (view == nullptr) {
- return FudStatus::NullPointer;
- }
-
- StringView sView{*view};
- auto skipResult = sView.trimWhitespace();
- if (skipResult.isError()) {
- return skipResult.getError();
- }
- view->data = sView.data;
- view->length = sView.length;
- return FudStatus::Success;
-}
-
-namespace impl {
-constexpr Array<int8_t, 256> AsciiLookup{
- {-1, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
- -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
- -2, -2, -2, -2, -2, -2, -2, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
- 32, 33, 34, 35, -2, -2, -2, -2, -2, -2, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28,
- 29, 30, 31, 32, 33, 34, 35, -2, -2, -2, -2, -2, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3,
- -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3,
- -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3,
- -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3,
- -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3}};
-
-// NOLINTBEGIN(readability-magic-numbers)
-static_assert(AsciiLookup[static_cast<size_t>('0')] == 0);
-static_assert(AsciiLookup[static_cast<size_t>('9')] == 9);
-static_assert(AsciiLookup[static_cast<size_t>('a')] == 10);
-static_assert(AsciiLookup[static_cast<size_t>('A')] == 10);
-static_assert(AsciiLookup[static_cast<size_t>('f')] == 15);
-static_assert(AsciiLookup[static_cast<size_t>('F')] == 15);
-static_assert(AsciiLookup[127] == -2);
-static_assert(AsciiLookup[128] == -3);
-static_assert(AsciiLookup[255] == -3);
-// NOLINTEND(readability-magic-numbers)
-
-FudStatus determineRadix(StringView input, uint8_t& radix, size_t& index)
-{
- if (input.length < 1) {
- return FudStatus::InvalidInput;
- }
-
- if (input.length == 1 && input.data[0] == '0') {
- radix = ExtRadixOctal;
- return FudStatus::Success;
- }
-
- if (input.length == 1) {
- radix = ExtRadixDecimal;
- return FudStatus::Success;
- }
-
- if (input.data[0] == '0' && (input.data[1] == 'x' || input.data[1] == 'X')) {
- radix = ExtRadixHexadecimal;
- index += 2;
- return FudStatus::Success;
- }
-
- if (input.data[0] == '0') {
- auto nextChar = input.data[1];
- auto nextVal = AsciiLookup[nextChar];
- if (nextVal >= 0 && nextVal < ExtRadixOctal) {
- radix = ExtRadixOctal;
- return FudStatus::Success;
- }
- if (nextVal >= ExtRadixOctal) {
- return FudStatus::InvalidInput;
- }
- }
-
- radix = ExtRadixDecimal;
- return FudStatus::Success;
-}
-
-FudStatus getRadix(StringView& view, uint8_t& radix, size_t& skipIndex)
-{
- if (radix == 0) {
- size_t radixIndex = 0;
- auto status = determineRadix(view, radix, radixIndex);
- if (status != FudStatus::Success) {
- return status;
- }
- skipIndex += radixIndex;
- view.data += radixIndex;
- view.length -= radixIndex;
- } else if (radix == ExtRadixHexadecimal && view.length > 2 && (view.data[1] == 'x' || view.data[1] == 'X')) {
- skipIndex += 2;
- view.data += 2;
- view.length -= 2;
- }
- return FudStatus::Success;
-}
-
-FudStatus checkNegative(StringView& view, bool& isNegative, size_t& skipIndex)
-{
- isNegative = view.data[0] == '-';
- if (isNegative && view.length == 1) {
- return FudStatus::InvalidInput;
- }
- if (isNegative) {
- skipIndex += 1;
- view.data++;
- view.length--;
- }
- return FudStatus::Success;
-}
-
-FudStatus checkPlusSigned(StringView& view, size_t& skipIndex)
-{
- auto isPlusSigned = view.data[0] == '+';
- if (isPlusSigned && view.length == 1) {
- return FudStatus::InvalidInput;
- }
- if (isPlusSigned) {
- skipIndex += 1;
- view.data++;
- view.length--;
- }
- return FudStatus::Success;
-}
-
-template <typename T>
-FudStatus stringViewToUnsignedInteger(StringView input, T& number, uint8_t specifiedRadix, size_t& index)
-{
- if (input.data == nullptr) {
- return FudStatus::NullPointer;
- }
-
- if (specifiedRadix == 1 || specifiedRadix > ExtMaxRadix || input.length < 1) {
- return FudStatus::InvalidInput;
- }
-
- uint8_t radix = specifiedRadix;
-
- StringView view{input};
- size_t skipIndex = 0;
- auto status = skipWhitespace(view, skipIndex);
- if (status != FudStatus::Success) {
- return status;
- }
-
- status = checkPlusSigned(view, skipIndex);
- if (status != FudStatus::Success) {
- return FudStatus::InvalidInput;
- }
-
- status = getRadix(view, radix, skipIndex);
-
- T num = 0;
- size_t digitIndex = 0;
-
- while (digitIndex < view.length) {
- auto digitResult = impl::AsciiLookup[view.data[digitIndex]];
- if (digitResult >= radix || digitResult < 0) {
- break;
- }
-
- auto digit = static_cast<uint8_t>(digitResult);
- if (std::numeric_limits<T>::max() / radix < num) {
- return FudStatus::InvalidInput;
- }
- num *= radix;
- if (std::numeric_limits<T>::max() - digit < num) {
- return FudStatus::InvalidInput;
- }
- num += digit;
- digitIndex++;
- }
- if (digitIndex < 1) {
- return FudStatus::InvalidInput;
- }
-
- index = skipIndex + digitIndex;
- number = num;
-
- return FudStatus::Success;
-}
-
-template <typename T>
-FudStatus stringViewToUnsignedInteger(StringView input, T* number, uint8_t specifiedRadix, size_t* index)
-{
- if (anyAreNull(input.data, number)) {
- return FudStatus::NullPointer;
- }
-
- size_t localIndex = 0;
-
- auto status = stringViewToUnsignedInteger(input, *number, specifiedRadix, localIndex);
- if (status == FudStatus::Success && index != nullptr) {
- *index = localIndex;
- }
- return status;
-}
-
-template <typename T>
-FudStatus viewToSignedIntPositive(StringView view, uint8_t radix, size_t& digitIndex, T& num)
-{
- digitIndex = 0;
- while (digitIndex < view.length) {
- int8_t digitResult = impl::AsciiLookup[view.data[digitIndex]];
- if (digitResult >= radix) {
- return FudStatus::InvalidInput;
- }
- if (digitResult < 0) {
- break;
- }
- auto digit = static_cast<uint8_t>(digitResult);
- if (std::numeric_limits<T>::max() / radix < num) {
- return FudStatus::InvalidInput;
- }
- num = static_cast<T>(num * radix);
- if (std::numeric_limits<T>::max() - digit < num) {
- return FudStatus::InvalidInput;
- }
- num = static_cast<T>(num + digit);
- digitIndex++;
- }
-
- return FudStatus::Success;
-}
-
-template <typename T>
-FudStatus viewToSignedIntNegative(StringView view, uint8_t radix, size_t& digitIndex, T& num)
-{
- digitIndex = 0;
- while (digitIndex < view.length) {
- int8_t digitResult = impl::AsciiLookup[view.data[digitIndex]];
- if (digitResult >= radix) {
- return FudStatus::InvalidInput;
- }
- if (digitResult < 0) {
- break;
- }
- auto digit = static_cast<uint8_t>(digitResult);
- if ((std::numeric_limits<T>::min() / radix > num)) {
- return FudStatus::InvalidInput;
- }
- num = static_cast<T>(num * radix);
- if (std::numeric_limits<T>::min() + digit > num) {
- return FudStatus::InvalidInput;
- }
- num = static_cast<T>(num - digit);
- digitIndex++;
- }
-
- return FudStatus::Success;
-}
-
-template <typename T>
-FudStatus stringViewToSignedInteger(StringView input, T& number, uint8_t specifiedRadix, size_t& index)
-{
- if (input.data == nullptr) {
- return FudStatus::NullPointer;
- }
-
- auto radix = specifiedRadix;
-
- StringView view{input};
- size_t skipIndex = 0;
- auto status = skipWhitespace(view, skipIndex);
- if (status != FudStatus::Success) {
- return status;
- }
-
- bool isNegative = false;
- status = checkNegative(view, isNegative, skipIndex);
- if (status != FudStatus::Success) {
- return FudStatus::InvalidInput;
- }
-
- if (!isNegative) {
- status = checkPlusSigned(view, skipIndex);
- if (status != FudStatus::Success) {
- return FudStatus::InvalidInput;
- }
- }
-
- status = getRadix(view, radix, skipIndex);
-
- T num = 0;
- size_t digitIndex = 0;
-
- if (isNegative) {
- status = viewToSignedIntNegative(view, radix, digitIndex, num);
- } else {
- status = viewToSignedIntPositive(view, radix, digitIndex, num);
- }
- if (status != FudStatus::Success) {
- return status;
- }
-
- if (digitIndex < 1) {
- return FudStatus::InvalidInput;
- }
-
- index = skipIndex + digitIndex;
- number = num;
- return FudStatus::Success;
-}
-
-template <typename T>
-FudStatus stringViewToSignedInteger(StringView input, T* number, uint8_t specifiedRadix, size_t* index)
-{
- if (anyAreNull(input.data, number)) {
- return FudStatus::NullPointer;
- }
-
- if (specifiedRadix == 1 || specifiedRadix > ExtMaxRadix || input.length < 1) {
- return FudStatus::InvalidInput;
- }
-
- size_t localIndex = 0;
- auto status = stringViewToSignedInteger(input, *number, specifiedRadix, localIndex);
- if (status == FudStatus::Success && index != nullptr) {
- *index = localIndex;
- }
- return status;
-}
-
-} // namespace impl
-
-FudStatus fud_string_to_uint8(StringView input, uint8_t* number, uint8_t specifiedRadix, size_t* index)
-{
- return impl::stringViewToUnsignedInteger(input, number, specifiedRadix, index);
-}
-
-FudStatus fud_string_to_uint16(StringView input, uint16_t* number, uint8_t specifiedRadix, size_t* index)
-{
- return impl::stringViewToUnsignedInteger(input, number, specifiedRadix, index);
-}
-
-FudStatus fud_string_to_uint32(StringView input, uint32_t* number, uint8_t specifiedRadix, size_t* index)
-{
- return impl::stringViewToUnsignedInteger(input, number, specifiedRadix, index);
-}
-
-FudStatus fud_string_to_uint64(StringView input, uint64_t* number, uint8_t specifiedRadix, size_t* index)
-{
- return impl::stringViewToUnsignedInteger(input, number, specifiedRadix, index);
-}
-
-FudStatus fud_string_to_int8(StringView input, int8_t* number, uint8_t specifiedRadix, size_t* index)
-{
- return impl::stringViewToSignedInteger(input, number, specifiedRadix, index);
-}
-
-FudStatus fud_string_to_int16(StringView input, int16_t* number, uint8_t specifiedRadix, size_t* index)
-{
- return impl::stringViewToSignedInteger(input, number, specifiedRadix, index);
-}
-
-FudStatus fud_string_to_int32(StringView input, int32_t* number, uint8_t specifiedRadix, size_t* index)
-{
- return impl::stringViewToSignedInteger(input, number, specifiedRadix, index);
-}
-
-FudStatus fud_string_to_int64(StringView input, int64_t* number, uint8_t specifiedRadix, size_t* index)
-{
- return impl::stringViewToSignedInteger(input, number, specifiedRadix, index);
-}
-
-namespace impl {
-
-template <typename T>
-bool isNanOrInf(T& num, StringView& view, T& sign, size_t& digitIndex)
-{
- if (view.length >= 3) {
- Array<uint8_t, 3> letters{{view.data[0], view.data[1], view.data[2]}};
- mapMut(letters, fud_char_to_lower);
- if (letters[0] == 'i' && letters[1] == 'n' && letters[2] == 'f') {
- num = sign * std::numeric_limits<T>::infinity();
- digitIndex = 3;
- return true;
- }
- if (letters[0] == 'n' && letters[1] == 'a' && letters[2] == 'n') {
- num = std::numeric_limits<T>::quiet_NaN();
- ;
- digitIndex = 3;
- return true;
- }
- }
- return false;
-}
-
-template <typename T>
-FudStatus getWhole(
- const StringView view,
- size_t& digitIndex,
- T& num,
- T sign,
- uint8_t radix,
- bool& foundDecimal,
- bool& foundExponent)
-{
- while (digitIndex < view.length) {
- auto nextChar = view.data[digitIndex];
- if (nextChar == '.') {
- foundDecimal = true;
- digitIndex++;
- break;
- }
-
- if (radix == ExtRadixDecimal && (nextChar == 'e' || nextChar == 'E')) {
- foundExponent = true;
- digitIndex++;
- break;
- }
-
- auto digitResult = impl::AsciiLookup[nextChar];
- if (digitResult >= radix) {
- return FudStatus::InvalidInput;
- }
- if (digitResult < 0) {
- break;
- }
- auto digit = static_cast<T>(digitResult) * sign;
- num *= static_cast<T>(radix);
-
- num += digit;
- digitIndex++;
- }
- return FudStatus::Success;
-}
-
-template <typename T>
-FudStatus getExponent(StringView view, size_t& digitIndex, T& num, uint8_t radix)
-{
- int32_t exponent{};
- StringView tempView{view.length - digitIndex, view.data + digitIndex};
- size_t exponentLength{};
- auto status = tempView.toInt32(exponent, ExtRadixDecimal, exponentLength);
- if (status != FudStatus::Success) {
- return status;
- }
- digitIndex += exponentLength;
- num = num * std::pow(static_cast<T>(radix), static_cast<T>(exponent));
- return FudStatus::Success;
-}
-
-template <typename T>
-FudStatus getFraction(const StringView view, size_t& digitIndex, T& num, T sign, uint8_t radix, bool& foundExponent)
-{
- auto radixDiv = 1.0F / static_cast<T>(radix);
- while (digitIndex < view.length) {
- auto nextChar = view.data[digitIndex];
- if (radix == ExtRadixDecimal && (nextChar == 'e' || nextChar == 'E')) {
- foundExponent = true;
- digitIndex++;
- break;
- }
-
- auto digitResult = impl::AsciiLookup[nextChar];
- if (digitResult >= radix) {
- return FudStatus::InvalidInput;
- }
- if (digitResult < 0) {
- break;
- }
- auto digit = static_cast<T>(digitResult) * sign;
- num += digit * radixDiv;
- radixDiv /= static_cast<T>(radix);
- digitIndex++;
- }
- return FudStatus::Success;
-}
-
-template <typename T>
-FudStatus stringViewToFloat(StringView input, T& number, size_t& index)
-{
- if (input.data == nullptr) {
- return FudStatus::NullPointer;
- }
-
- if (input.length < 1) {
- return FudStatus::InvalidInput;
- }
-
- uint8_t radix = 0;
-
- StringView view{input};
- size_t skipIndex = 0;
-
- auto status = skipWhitespace(view, skipIndex);
- if (status != FudStatus::Success) {
- return status;
- }
-
- T sign = 1.0;
- bool isNegative = false;
- status = impl::checkNegative(view, isNegative, skipIndex);
- if (status != FudStatus::Success) {
- return FudStatus::InvalidInput;
- }
-
- if (!isNegative) {
- status = checkPlusSigned(view, skipIndex);
- } else {
- sign = -1.0;
- }
-
- if (status != FudStatus::Success) {
- return FudStatus::InvalidInput;
- }
-
- T num = 0;
- size_t digitIndex = 0;
-
- auto retSuccess = [&]() {
- index = skipIndex + digitIndex;
- number = num;
- return FudStatus::Success;
- };
-
- if (impl::isNanOrInf(num, view, sign, digitIndex)) {
- return retSuccess();
- }
-
- status = impl::getRadix(view, radix, skipIndex);
- if (status != FudStatus::Success) {
- return status;
- }
-
- bool foundDecimal = false;
- bool foundExponent = false;
- status = getWhole(view, digitIndex, num, sign, radix, foundDecimal, foundExponent);
-
- if (status == FudStatus::Success && foundExponent) {
- status = getExponent(view, digitIndex, num, radix);
- }
-
- if (status != FudStatus::Success) {
- return status;
- }
-
- if (!foundDecimal) {
- if (digitIndex < 1) {
- return FudStatus::InvalidInput;
- }
-
- return retSuccess();
- }
-
- status = getFraction(view, digitIndex, num, sign, radix, foundExponent);
-
- if (foundExponent) {
- status = getExponent(view, digitIndex, num, radix);
- if (status != FudStatus::Success) {
- return status;
- }
- }
-
- if (digitIndex < 1) {
- return FudStatus::InvalidInput;
- }
-
- if (std::isinf(num) || std::isnan(num)) // isnan is dubious here - likely unreachable
- {
- return FudStatus::RangeError;
- }
-
- return retSuccess();
-}
-
-template <typename T>
-FudStatus stringViewToFloat(StringView input, T* number, size_t* index)
-{
- if (anyAreNull(input.data, number)) {
- return FudStatus::NullPointer;
- }
-
- size_t localIndex{0};
- auto status = stringViewToFloat(input, *number, localIndex);
-
- if (status == FudStatus::Success && index != nullptr) {
- *index = localIndex;
- }
- return status;
-}
-
-} // namespace impl
-
-FudStatus fud_string_to_float(StringView input, float* number, size_t* index)
-{
- return impl::stringViewToFloat(input, number, index);
-}
-
-FudStatus fud_string_to_double(StringView input, double* number, size_t* index)
-{
- return impl::stringViewToFloat(input, number, index);
-}
-
-namespace fud {
-
-FudStatus StringView::toUint8(uint8_t& number, uint8_t specifiedRadix, size_t& strLen) const
-{
- return ::impl::stringViewToUnsignedInteger(*this, number, specifiedRadix, strLen);
-}
-
-FudStatus StringView::toUint16(uint16_t& number, uint8_t specifiedRadix, size_t& strLen) const
-{
- return ::impl::stringViewToUnsignedInteger(*this, number, specifiedRadix, strLen);
-}
-
-FudStatus StringView::toUint32(uint32_t& number, uint8_t specifiedRadix, size_t& strLen) const
-{
- return ::impl::stringViewToUnsignedInteger(*this, number, specifiedRadix, strLen);
-}
-
-FudStatus StringView::toUint64(uint64_t& number, uint8_t specifiedRadix, size_t& strLen) const
-{
- return ::impl::stringViewToUnsignedInteger(*this, number, specifiedRadix, strLen);
-}
-
-FudStatus StringView::toInt8(int8_t& number, uint8_t specifiedRadix, size_t& strLen) const
-{
- return ::impl::stringViewToSignedInteger(*this, number, specifiedRadix, strLen);
-}
-
-FudStatus StringView::toInt16(int16_t& number, uint8_t specifiedRadix, size_t& strLen) const
-{
- return ::impl::stringViewToSignedInteger(*this, number, specifiedRadix, strLen);
-}
-
-FudStatus StringView::toInt32(int32_t& number, uint8_t specifiedRadix, size_t& strLen) const
-{
- return ::impl::stringViewToSignedInteger(*this, number, specifiedRadix, strLen);
-}
-
-FudStatus StringView::toInt64(int64_t& number, uint8_t specifiedRadix, size_t& strLen) const
-{
- return ::impl::stringViewToSignedInteger(*this, number, specifiedRadix, strLen);
-}
-
-FudStatus StringView::toFloat(float& number, size_t& strLen) const
-{
- return ::impl::stringViewToFloat(*this, number, strLen);
-}
-
-FudStatus StringView::toDouble(double& number, size_t& strLen) const
-{
- return ::impl::stringViewToFloat(*this, number, strLen);
-}
-
#endif
} // namespace fud
diff --git a/source/fud_utf8.cpp b/source/fud_utf8.cpp
index 4d617da..bffb5c1 100644
--- a/source/fud_utf8.cpp
+++ b/source/fud_utf8.cpp
@@ -27,7 +27,6 @@ FudUtf8 FudUtf8::from(const String& fudString, size_t index) noexcept
return invalidAscii();
}
-
return from(StringView{fudString}, index);
}
@@ -69,20 +68,26 @@ FudUtf8 FudUtf8::from(StringView view, size_t index) noexcept
return invalidAscii();
}
-bool charIsAscii(char character)
+namespace classify {
+
+bool isAscii(char character)
+{
+ return isAscii(static_cast<utf8>(character));
+}
+
+bool isAscii(utf8 character)
{
- return static_cast<uint8_t>(character & ~ASCII_MASK) == 0;
+ return (character & ~ASCII_MASK) == 0;
}
-bool utf8IsAscii(FudUtf8 character)
+bool isAscii(FudUtf8 character)
{
return character.getType() == Utf8Type::Ascii && character.valid();
}
namespace impl {
-template <typename Predicate>
-bool isAsciiPredicate(FudUtf8 character, Predicate&& predicate)
+bool isAsciiPredicate(FudUtf8 character, bool (*predicate)(char))
{
auto maybeAscii = character.getAscii();
if (!maybeAscii.has_value()) {
@@ -90,92 +95,122 @@ bool isAsciiPredicate(FudUtf8 character, Predicate&& predicate)
}
auto asciiChar = *maybeAscii;
- return std::forward<Predicate>(predicate)(asciiChar.asChar());
+ return predicate(asciiChar.asChar());
}
} // namespace impl
-bool charIsAlphanumeric(char character)
+bool isAlphanumeric(char character)
{
- if (!charIsAscii(character)) {
+ return isAlphanumeric(static_cast<utf8>(character));
+}
+
+bool isAlphanumeric(utf8 character)
+{
+ if (!isAscii(character)) {
return false;
}
- if (charIsAlpha(character)) {
+ if (isAlpha(character)) {
return true;
}
- return charIsDigit(character);
+ return isDigit(character);
+}
+
+bool isAlphanumeric(FudUtf8 character)
+{
+ return impl::isAsciiPredicate(character, isAlphanumeric);
}
-bool utf8IsAlphanumeric(FudUtf8 character)
+bool isAlpha(char character)
{
- return impl::isAsciiPredicate(character, charIsAlphanumeric);
+ return isAlpha(static_cast<utf8>(character));
}
-bool charIsAlpha(char character)
+bool isAlpha(utf8 character)
{
- if (!charIsAscii(character)) {
+ if (!isAscii(character)) {
return false;
}
- if (charIsUppercase(character)) {
+ if (isUppercase(character)) {
return true;
}
- return charIsLowercase(character);
+ return isLowercase(character);
}
-bool utf8IsAlpha(FudUtf8 character)
+bool isAlpha(FudUtf8 character)
{
- return impl::isAsciiPredicate(character, charIsAlpha);
+ return impl::isAsciiPredicate(character, isAlpha);
}
-bool charIsLowercase(char character)
+bool isLowercase(char character)
{
- if (!charIsAscii(character)) {
+ return isLowercase(static_cast<utf8>(character));
+}
+
+bool isLowercase(utf8 character)
+{
+ if (!isAscii(character)) {
return false;
}
return 'a' <= character && character <= 'z';
}
-bool utf8IsLowercase(FudUtf8 character)
+bool isLowercase(FudUtf8 character)
+{
+ return impl::isAsciiPredicate(character, isLowercase);
+}
+
+bool isUppercase(char character)
{
- return impl::isAsciiPredicate(character, charIsLowercase);
+ return isUppercase(static_cast<utf8>(character));
}
-bool charIsUppercase(char character)
+bool isUppercase(utf8 character)
{
- if (!charIsAscii(character)) {
+ if (!isAscii(character)) {
return false;
}
return 'A' <= character && character <= 'Z';
}
-bool utf8IsUppercase(FudUtf8 character)
+bool isUppercase(FudUtf8 character)
{
- return impl::isAsciiPredicate(character, charIsUppercase);
+ return impl::isAsciiPredicate(character, isUppercase);
}
-bool charIsDigit(char character)
+bool isDigit(char character)
{
- if (!charIsAscii(character)) {
+ return isDigit(static_cast<utf8>(character));
+}
+
+bool isDigit(utf8 character)
+{
+ if (!isAscii(character)) {
return false;
}
return '0' <= character && character <= '9';
}
-bool utf8IsDigit(FudUtf8 character)
+bool isDigit(FudUtf8 character)
+{
+ return impl::isAsciiPredicate(character, isDigit);
+}
+
+bool isHexDigit(char character)
{
- return impl::isAsciiPredicate(character, charIsDigit);
+ return isHexDigit(static_cast<utf8>(character));
}
-bool charIsHexDigit(char character)
+bool isHexDigit(utf8 character)
{
- if (!charIsAscii(character)) {
+ if (!isAscii(character)) {
return false;
}
@@ -183,86 +218,116 @@ bool charIsHexDigit(char character)
('A' <= character && character <= 'F');
}
-bool utf8IsHexDigit(FudUtf8 character)
+bool isHexDigit(FudUtf8 character)
+{
+ return impl::isAsciiPredicate(character, isHexDigit);
+}
+
+bool isControl(char character)
{
- return impl::isAsciiPredicate(character, charIsHexDigit);
+ return isControl(static_cast<utf8>(character));
}
-bool charIsControl(char character)
+bool isControl(utf8 character)
{
- if (!charIsAscii(character)) {
+ if (!isAscii(character)) {
return false;
}
constexpr char maxControlChar = 0x1F;
constexpr const char deleteChar = 0x7F;
- return ((static_cast<uint8_t>(character) <= maxControlChar)) || character == deleteChar;
+ return ((static_cast<utf8>(character) <= maxControlChar)) || character == deleteChar;
}
-bool utf8IsControl(FudUtf8 character)
+bool isControl(FudUtf8 character)
{
- return impl::isAsciiPredicate(character, charIsControl);
+ return impl::isAsciiPredicate(character, isControl);
}
-bool charIsGraphical(char character)
+bool isGraphical(char character)
{
- if (!charIsAscii(character)) {
+ return isGraphical(static_cast<utf8>(character));
+}
+
+bool isGraphical(utf8 character)
+{
+ if (!isAscii(character)) {
return false;
}
- return charIsAlphanumeric(character) || charIsPunctuation(character);
+ return isAlphanumeric(character) || isPunctuation(character);
+}
+
+bool isGraphical(FudUtf8 character)
+{
+ return impl::isAsciiPredicate(character, isGraphical);
}
-bool utf8IsGraphical(FudUtf8 character)
+bool isSpace(char character)
{
- return impl::isAsciiPredicate(character, charIsGraphical);
+ return isSpace(static_cast<utf8>(character));
}
-bool charIsSpace(char character)
+bool isSpace(utf8 character)
{
- if (!charIsAscii(character)) {
+ if (!isAscii(character)) {
return false;
}
return character == ' ' || character == '\t' || character == '\n' || character == '\r' || character == '\v';
}
-bool utf8IsSpace(FudUtf8 character)
+bool isSpace(FudUtf8 character)
{
- return impl::isAsciiPredicate(character, charIsSpace);
+ return impl::isAsciiPredicate(character, isSpace);
}
-bool charIsBlank(char character)
+bool isBlank(char character)
{
- if (!charIsAscii(character)) {
+ return isBlank(static_cast<utf8>(character));
+}
+
+bool isBlank(utf8 character)
+{
+ if (!isAscii(character)) {
return false;
}
return character == ' ' || character == '\t';
}
-bool utf8IsBlank(FudUtf8 character)
+bool isBlank(FudUtf8 character)
+{
+ return impl::isAsciiPredicate(character, isBlank);
+}
+
+bool isPrintable(char character)
{
- return impl::isAsciiPredicate(character, charIsBlank);
+ return isPrintable(static_cast<utf8>(character));
}
-bool charIsPrintable(char character)
+bool isPrintable(utf8 character)
{
- if (!charIsAscii(character)) {
+ if (!isAscii(character)) {
return false;
}
return (character >= ' ' && character <= '~');
}
-bool utf8IsPrintable(FudUtf8 character)
+bool isPrintable(FudUtf8 character)
{
- return impl::isAsciiPredicate(character, charIsPrintable);
+ return impl::isAsciiPredicate(character, isPrintable);
}
-bool charIsPunctuation(char character)
+bool isPunctuation(char character)
{
- if (!charIsAscii(character)) {
+ return isPunctuation(static_cast<utf8>(character));
+}
+
+bool isPunctuation(utf8 character)
+{
+ if (!isAscii(character)) {
return false;
}
@@ -270,45 +335,45 @@ bool charIsPunctuation(char character)
(character >= '[' && character <= '`') || (character >= '{' && character <= '~');
}
-bool utf8IsPunctuation(FudUtf8 character)
+bool isPunctuation(FudUtf8 character)
{
- return impl::isAsciiPredicate(character, charIsPunctuation);
+ return impl::isAsciiPredicate(character, isPunctuation);
}
+} // namespace classify
+
uint8_t charToLower(uint8_t character)
{
- if (charIsUppercase(static_cast<char>(character))) {
+ if (classify::isUppercase(static_cast<char>(character))) {
constexpr uint8_t lowerA = 'a';
constexpr uint8_t upperA = 'A';
- return static_cast<uint8_t>(character - upperA) + lowerA;
+ return static_cast<utf8>(character - upperA) + lowerA;
}
return character;
}
FudUtf8 utf8ToLower(FudUtf8 character)
{
- static_cast<void>(character.transformAscii([](Ascii& ascii) {
- ascii = Ascii{charToLower(static_cast<uint8_t>(ascii.asChar()))};
- }));
+ static_cast<void>(
+ character.transformAscii([](Ascii& ascii) { ascii = Ascii{charToLower(static_cast<utf8>(ascii.asChar()))}; }));
return character;
}
uint8_t charToUpper(uint8_t character)
{
- if (charIsLowercase(static_cast<char>(character))) {
+ if (classify::isLowercase(static_cast<char>(character))) {
constexpr uint8_t lowerA = 'a';
constexpr uint8_t upperA = 'A';
- return static_cast<uint8_t>(character - lowerA) + upperA;
+ return static_cast<utf8>(character - lowerA) + upperA;
}
return character;
}
FudUtf8 utf8ToUpper(FudUtf8 character)
{
- static_cast<void>(character.transformAscii([](Ascii& ascii) {
- ascii = Ascii{charToUpper(static_cast<uint8_t>(ascii.asChar()))};
- }));
+ static_cast<void>(
+ character.transformAscii([](Ascii& ascii) { ascii = Ascii{charToUpper(static_cast<utf8>(ascii.asChar()))}; }));
return character;
}
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 515ae16..788e4ba 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -69,6 +69,7 @@ fud_add_test(test_sqlite SOURCES test_sqlite.cpp)
fud_add_test(test_string SOURCES test_string.cpp)
fud_add_test(test_utf8 SOURCES test_utf8.cpp)
fud_add_test(test_vector SOURCES test_vector.cpp)
+fud_add_test(test_string_convert SOURCES test_string_convert.cpp)
# fud_add_test(test_deserialize_number SOURCES test_deserialize_number.cpp)
# fud_add_test(test_ext_algorithm SOURCES test_algorithm.cpp)
diff --git a/test/test_format.cpp b/test/test_format.cpp
index 319ed22..68c94cd 100644
--- a/test/test_format.cpp
+++ b/test/test_format.cpp
@@ -15,17 +15,339 @@
* limitations under the License.
*/
-// #include "fud_array.hpp"
-// #include "fud_format.hpp"
-// #include "fud_span.hpp"
+#include "fud_string_view.hpp"
+#include "fud_format.hpp"
#include "gtest/gtest.h"
namespace fud {
-TEST(FormatTest, FormatSpecTest)
+TEST(FormatTest, BasePositionalTest)
{
+ size_t length = 0;
+ auto formatSpecResult = FormatSpec::make(StringView{" {1:}"}, length);
+ ASSERT_TRUE(formatSpecResult.isError());
+ EXPECT_EQ(formatSpecResult.takeError(), FudStatus::ArgumentInvalid);
+
+ formatSpecResult = FormatSpec::make(StringView{"{1}"}, length);
+ ASSERT_TRUE(formatSpecResult.isOkay());
+ auto formatSpec = formatSpecResult.takeOkay();
+ EXPECT_EQ(formatSpec.position, 1);
+ EXPECT_EQ(length, 3);
+
+ formatSpecResult = FormatSpec::make(StringView{"{1:}"}, length);
+ ASSERT_TRUE(formatSpecResult.isOkay());
+ formatSpec = formatSpecResult.takeOkay();
+ EXPECT_EQ(length, 4);
+ EXPECT_EQ(formatSpec.position, 1);
+ EXPECT_EQ(formatSpec.fill.align(), FormatAlign::Value::Default);
+ EXPECT_EQ(formatSpec.fill.fill, ' ');
+ EXPECT_EQ(formatSpec.formatSign, FormatSign::Default);
+ EXPECT_FALSE(formatSpec.takesWidth);
+ EXPECT_FALSE(formatSpec.takesPrecision);
+ EXPECT_FALSE(formatSpec.alternateForm);
+ EXPECT_FALSE(formatSpec.leadingZero);
+ EXPECT_FALSE(formatSpec.hasLocale);
+ EXPECT_TRUE(std::holds_alternative<std::monostate>(formatSpec.formatType));
+}
+
+TEST(FormatTest, AlignTest)
+{
+ size_t length = 0;
+
+ auto formatSpecResult = FormatSpec::make(StringView{"{1:<}"}, length);
+ ASSERT_TRUE(formatSpecResult.isOkay());
+ auto formatSpec = formatSpecResult.takeOkay();
+ EXPECT_EQ(formatSpec.position, 1);
+ EXPECT_EQ(length, 5);
+ EXPECT_EQ(formatSpec.fill.align(), FormatAlign::Value::Left);
+ EXPECT_EQ(formatSpec.fill.fill, ' ');
+ EXPECT_FALSE(formatSpec.takesWidth);
+ EXPECT_FALSE(formatSpec.takesPrecision);
+ EXPECT_FALSE(formatSpec.alternateForm);
+ EXPECT_FALSE(formatSpec.leadingZero);
+ EXPECT_FALSE(formatSpec.hasLocale);
+ EXPECT_TRUE(std::holds_alternative<std::monostate>(formatSpec.formatType));
+
+ formatSpecResult = FormatSpec::make(StringView{"{1:<<}"}, length);
+ ASSERT_TRUE(formatSpecResult.isOkay());
+ formatSpec = formatSpecResult.takeOkay();
+ EXPECT_EQ(formatSpec.position, 1);
+ EXPECT_EQ(formatSpec.fill.align(), FormatAlign::Value::Left);
+ EXPECT_EQ(formatSpec.fill.fill, '<');
+
+ formatSpecResult = FormatSpec::make(StringView{"{:<<}"}, length);
+ ASSERT_TRUE(formatSpecResult.isOkay());
+ formatSpec = formatSpecResult.takeOkay();
+ EXPECT_EQ(length, 5);
+ EXPECT_EQ(formatSpec.position, FormatSpec::positionUnspecified);
+ EXPECT_EQ(formatSpec.fill.align(), FormatAlign::Value::Left);
+ EXPECT_EQ(formatSpec.fill.fill, '<');
+
+ formatSpecResult = FormatSpec::make(StringView{"{1:_<}"}, length);
+ ASSERT_TRUE(formatSpecResult.isOkay());
+ formatSpec = formatSpecResult.takeOkay();
+ EXPECT_EQ(length, 6);
+ EXPECT_EQ(formatSpec.position, 1);
+ EXPECT_EQ(formatSpec.fill.align(), FormatAlign::Value::Left);
+ EXPECT_EQ(formatSpec.fill.fill, '_');
+}
+
+TEST(FormatTest, SpecialTest)
+{
+ size_t length = 0;
+
+ auto formatSpecResult = FormatSpec::make(StringView{"{1:_< }"}, length);
+ ASSERT_TRUE(formatSpecResult.isOkay());
+ auto formatSpec = formatSpecResult.takeOkay();
+ EXPECT_EQ(length, 7);
+ EXPECT_EQ(formatSpec.position, 1);
+ EXPECT_EQ(formatSpec.fill.align(), FormatAlign::Value::Left);
+ EXPECT_EQ(formatSpec.fill.fill, '_');
+ EXPECT_EQ(formatSpec.formatSign, FormatSign::Space);
+ EXPECT_FALSE(formatSpec.alternateForm);
+ EXPECT_FALSE(formatSpec.leadingZero);
+
+ formatSpecResult = FormatSpec::make(StringView{"{1:+}"}, length);
+ ASSERT_TRUE(formatSpecResult.isOkay());
+ EXPECT_EQ(length, 5);
+ formatSpec = formatSpecResult.takeOkay();
+ EXPECT_EQ(formatSpec.position, 1);
+ EXPECT_EQ(formatSpec.formatSign, FormatSign::Plus);
+ EXPECT_FALSE(formatSpec.alternateForm);
+ EXPECT_FALSE(formatSpec.leadingZero);
+
+ formatSpecResult = FormatSpec::make(StringView{"{1:-}"}, length);
+ ASSERT_TRUE(formatSpecResult.isOkay());
+ formatSpec = formatSpecResult.takeOkay();
+ EXPECT_EQ(length, 5);
+ EXPECT_EQ(formatSpec.position, 1);
+ EXPECT_EQ(formatSpec.formatSign, FormatSign::Minus);
+ EXPECT_FALSE(formatSpec.alternateForm);
+ EXPECT_FALSE(formatSpec.leadingZero);
+
+ formatSpecResult = FormatSpec::make(StringView{"{1:_<#}"}, length);
+ ASSERT_TRUE(formatSpecResult.isOkay());
+ formatSpec = formatSpecResult.takeOkay();
+ EXPECT_EQ(length, 7);
+ EXPECT_EQ(formatSpec.position, 1);
+ EXPECT_EQ(formatSpec.fill.align(), FormatAlign::Value::Left);
+ EXPECT_EQ(formatSpec.fill.fill, '_');
+ EXPECT_EQ(formatSpec.formatSign, FormatSign::Default);
+ EXPECT_TRUE(formatSpec.alternateForm);
+ EXPECT_FALSE(formatSpec.leadingZero);
+
+ formatSpecResult = FormatSpec::make(StringView{"{1:_<0}"}, length);
+ ASSERT_TRUE(formatSpecResult.isOkay());
+ formatSpec = formatSpecResult.takeOkay();
+ EXPECT_EQ(length, 7);
+ EXPECT_EQ(formatSpec.position, 1);
+ EXPECT_EQ(formatSpec.fill.align(), FormatAlign::Value::Left);
+ EXPECT_EQ(formatSpec.fill.fill, '_');
+ EXPECT_EQ(formatSpec.formatSign, FormatSign::Default);
+ EXPECT_FALSE(formatSpec.alternateForm);
+ EXPECT_TRUE(formatSpec.leadingZero);
+
+ formatSpecResult = FormatSpec::make(StringView{"{1: #}"}, length);
+ ASSERT_TRUE(formatSpecResult.isOkay());
+ formatSpec = formatSpecResult.takeOkay();
+ EXPECT_EQ(length, 6);
+ EXPECT_EQ(formatSpec.position, 1);
+ EXPECT_EQ(formatSpec.fill.align(), FormatAlign::Value::Default);
+ EXPECT_EQ(formatSpec.fill.fill, ' ');
+ EXPECT_EQ(formatSpec.formatSign, FormatSign::Space);
+ EXPECT_TRUE(formatSpec.alternateForm);
+ EXPECT_FALSE(formatSpec.leadingZero);
+
+ formatSpecResult = FormatSpec::make(StringView{"{1:# }"}, length);
+ ASSERT_TRUE(formatSpecResult.isError());
+ EXPECT_EQ(formatSpecResult.takeError(), FudStatus::FormatInvalid);
+
+ formatSpecResult = FormatSpec::make(StringView{"{1:##}"}, length);
+ ASSERT_TRUE(formatSpecResult.isError());
+ EXPECT_EQ(formatSpecResult.takeError(), FudStatus::FormatInvalid);
+
+ formatSpecResult = FormatSpec::make(StringView{"{1: 0}"}, length);
+ ASSERT_TRUE(formatSpecResult.isOkay());
+ formatSpec = formatSpecResult.takeOkay();
+ EXPECT_EQ(length, 6);
+ EXPECT_EQ(formatSpec.position, 1);
+ EXPECT_EQ(formatSpec.fill.align(), FormatAlign::Value::Default);
+ EXPECT_EQ(formatSpec.fill.fill, ' ');
+ EXPECT_EQ(formatSpec.formatSign, FormatSign::Space);
+ EXPECT_FALSE(formatSpec.alternateForm);
+ EXPECT_TRUE(formatSpec.leadingZero);
+
+ formatSpecResult = FormatSpec::make(StringView{"{1:0 }"}, length);
+ ASSERT_TRUE(formatSpecResult.isError());
+ EXPECT_EQ(formatSpecResult.takeError(), FudStatus::FormatInvalid);
+
+ formatSpecResult = FormatSpec::make(StringView{"{1:0#}"}, length);
+ ASSERT_TRUE(formatSpecResult.isError());
+ EXPECT_EQ(formatSpecResult.takeError(), FudStatus::FormatInvalid);
+
+ formatSpecResult = FormatSpec::make(StringView{"{1:#00}"}, length);
+ ASSERT_TRUE(formatSpecResult.isError());
+ EXPECT_EQ(formatSpecResult.takeError(), FudStatus::FormatInvalid);
+
+ formatSpecResult = FormatSpec::make(StringView{"{1: #0}"}, length);
+ ASSERT_TRUE(formatSpecResult.isOkay());
+ formatSpec = formatSpecResult.takeOkay();
+ EXPECT_EQ(length, 7);
+ EXPECT_EQ(formatSpec.position, 1);
+ EXPECT_EQ(formatSpec.fill.align(), FormatAlign::Value::Default);
+ EXPECT_EQ(formatSpec.fill.fill, ' ');
+ EXPECT_EQ(formatSpec.formatSign, FormatSign::Space);
+ EXPECT_TRUE(formatSpec.alternateForm);
+ EXPECT_TRUE(formatSpec.leadingZero);
+}
+
+TEST(FormatTest, WidthTest)
+{
+ size_t length = 0;
+
+ auto formatSpecResult = FormatSpec::make(StringView{"{1:1}"}, length);
+ ASSERT_TRUE(formatSpecResult.isOkay());
+ auto formatSpec = formatSpecResult.takeOkay();
+ EXPECT_EQ(length, 5);
+ EXPECT_EQ(formatSpec.position, 1);
+ EXPECT_FALSE(formatSpec.takesWidth);
+ EXPECT_EQ(formatSpec.width, 1);
+
+ formatSpecResult = FormatSpec::make(StringView{"{1:543}"}, length);
+ ASSERT_TRUE(formatSpecResult.isOkay());
+ formatSpec = formatSpecResult.takeOkay();
+ EXPECT_EQ(length, 7);
+ EXPECT_EQ(formatSpec.position, 1);
+ EXPECT_FALSE(formatSpec.takesWidth);
+ EXPECT_EQ(formatSpec.width, 543);
+
+ formatSpecResult = FormatSpec::make(StringView{"{:543}"}, length);
+ ASSERT_TRUE(formatSpecResult.isOkay());
+ formatSpec = formatSpecResult.takeOkay();
+ EXPECT_EQ(length, 6);
+ EXPECT_EQ(formatSpec.position, FormatSpec::positionUnspecified);
+ EXPECT_FALSE(formatSpec.takesWidth);
+ EXPECT_EQ(formatSpec.width, 543);
+
+ // leading zero
+ formatSpecResult = FormatSpec::make(StringView{"{1:00}"}, length);
+ ASSERT_TRUE(formatSpecResult.isError());
+ EXPECT_EQ(formatSpecResult.getError(), FudStatus::FormatInvalid);
+
+ // #x100000000 4294967296
+ formatSpecResult = FormatSpec::make(StringView{"{1:4294967296}"}, length);
+ ASSERT_TRUE(formatSpecResult.isError());
+ EXPECT_EQ(formatSpecResult.getError(), FudStatus::RangeError);
+
+ formatSpecResult = FormatSpec::make(StringView{"{1:{1}}"}, length);
+ ASSERT_TRUE(formatSpecResult.isOkay());
+ formatSpec = formatSpecResult.takeOkay();
+ EXPECT_EQ(length, 7);
+ EXPECT_EQ(formatSpec.position, 1);
+ EXPECT_TRUE(formatSpec.takesWidth);
+ EXPECT_EQ(formatSpec.width, 1);
+
+ // #x10000 65536
+ formatSpecResult = FormatSpec::make(StringView{"{1:{65536}}"}, length);
+ ASSERT_TRUE(formatSpecResult.isError());
+ EXPECT_EQ(formatSpecResult.getError(), FudStatus::RangeError);
+
+ formatSpecResult = FormatSpec::make(StringView{"{:{1}}"}, length);
+ ASSERT_TRUE(formatSpecResult.isError());
+ EXPECT_EQ(formatSpecResult.getError(), FudStatus::FormatInvalid);
+
+ formatSpecResult = FormatSpec::make(StringView{"{:{}}"}, length);
+ ASSERT_TRUE(formatSpecResult.isOkay());
+ formatSpec = formatSpecResult.takeOkay();
+ EXPECT_EQ(length, 5);
+ EXPECT_EQ(formatSpec.position, FormatSpec::positionUnspecified);
+ EXPECT_TRUE(formatSpec.takesWidth);
+ EXPECT_EQ(formatSpec.width, FormatSpec::widthUnspecified);
+}
+
+TEST(FormatTest, PrecisionTest)
+{
+ size_t length = 0;
+
+ auto formatSpecResult = FormatSpec::make(StringView{"{1:.1}"}, length);
+ ASSERT_TRUE(formatSpecResult.isOkay());
+ auto formatSpec = formatSpecResult.takeOkay();
+ EXPECT_EQ(length, 6);
+ EXPECT_EQ(formatSpec.position, 1);
+ EXPECT_FALSE(formatSpec.takesPrecision);
+ EXPECT_EQ(formatSpec.precision, 1);
+
+ formatSpecResult = FormatSpec::make(StringView{"{1:.543}"}, length);
+ ASSERT_TRUE(formatSpecResult.isOkay());
+ formatSpec = formatSpecResult.takeOkay();
+ EXPECT_EQ(length, 8);
+ EXPECT_EQ(formatSpec.position, 1);
+ EXPECT_FALSE(formatSpec.takesPrecision);
+ EXPECT_EQ(formatSpec.precision, 543);
+
+ formatSpecResult = FormatSpec::make(StringView{"{:.543}"}, length);
+ ASSERT_TRUE(formatSpecResult.isOkay());
+ formatSpec = formatSpecResult.takeOkay();
+ EXPECT_EQ(length, 7);
+ EXPECT_EQ(formatSpec.position, FormatSpec::positionUnspecified);
+ EXPECT_FALSE(formatSpec.takesPrecision);
+ EXPECT_EQ(formatSpec.precision, 543);
+
+ // leading zero
+ formatSpecResult = FormatSpec::make(StringView{"{1:00}"}, length);
+ ASSERT_TRUE(formatSpecResult.isError());
+ EXPECT_EQ(formatSpecResult.getError(), FudStatus::FormatInvalid);
+
+ // #x100000000 4294967296
+ formatSpecResult = FormatSpec::make(StringView{"{1:.4294967296}"}, length);
+ ASSERT_TRUE(formatSpecResult.isError());
+ EXPECT_EQ(formatSpecResult.getError(), FudStatus::RangeError);
+
+ formatSpecResult = FormatSpec::make(StringView{"{1:.{1}}"}, length);
+ ASSERT_TRUE(formatSpecResult.isOkay());
+ formatSpec = formatSpecResult.takeOkay();
+ EXPECT_EQ(length, 8);
+ EXPECT_EQ(formatSpec.position, 1);
+ EXPECT_TRUE(formatSpec.takesPrecision);
+ EXPECT_EQ(formatSpec.precision, 1);
+
+ // #x10000 65536
+ formatSpecResult = FormatSpec::make(StringView{"{1:.{65536}}"}, length);
+ ASSERT_TRUE(formatSpecResult.isError());
+ EXPECT_EQ(formatSpecResult.getError(), FudStatus::RangeError);
+
+ formatSpecResult = FormatSpec::make(StringView{"{:.{1}}"}, length);
+ ASSERT_TRUE(formatSpecResult.isError());
+ EXPECT_EQ(formatSpecResult.getError(), FudStatus::FormatInvalid);
+
+ formatSpecResult = FormatSpec::make(StringView{"{:.{}}"}, length);
+ ASSERT_TRUE(formatSpecResult.isOkay());
+ formatSpec = formatSpecResult.takeOkay();
+ EXPECT_EQ(length, 6);
+ EXPECT_EQ(formatSpec.position, FormatSpec::positionUnspecified);
+ EXPECT_TRUE(formatSpec.takesPrecision);
+ EXPECT_EQ(formatSpec.precision, FormatSpec::widthUnspecified);
+}
+
+TEST(FormatTest, LocaleTest)
+{
+ size_t length = 0;
+
+ auto formatSpecResult = FormatSpec::make(StringView{"{1:L}"}, length);
+ ASSERT_TRUE(formatSpecResult.isOkay());
+ auto formatSpec = formatSpecResult.takeOkay();
+ EXPECT_EQ(length, 5);
+ EXPECT_EQ(formatSpec.position, 1);
+ EXPECT_TRUE(formatSpec.hasLocale);
+
+ formatSpecResult = FormatSpec::make(StringView{"{:L}"}, length);
+ ASSERT_TRUE(formatSpecResult.isOkay());
+ formatSpec = formatSpecResult.takeOkay();
+ EXPECT_EQ(length, 4);
+ EXPECT_EQ(formatSpec.position, FormatSpec::positionUnspecified);
+ EXPECT_TRUE(formatSpec.hasLocale);
}
} // namespace fud
diff --git a/test/test_string_convert.cpp b/test/test_string_convert.cpp
new file mode 100644
index 0000000..38f5123
--- /dev/null
+++ b/test/test_string_convert.cpp
@@ -0,0 +1,186 @@
+/*
+ * libfud
+ * Copyright 2024 Dominick Allen
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#include "fud_string_convert.hpp"
+
+#include "gtest/gtest.h"
+
+namespace fud {
+
+TEST(FudStringConvert, ConvertInt)
+{
+ auto intResult = fromString<int>(StringView{"0"});
+ ASSERT_TRUE(intResult.isOkay());
+ auto convertState = intResult.getOkay();
+ ASSERT_EQ(convertState.value, 0);
+ ASSERT_EQ(convertState.nextIndex, 1);
+
+ intResult = fromString<int>(StringView{"1"});
+ ASSERT_TRUE(intResult.isOkay());
+ convertState = intResult.getOkay();
+ ASSERT_EQ(convertState.value, 1);
+ ASSERT_EQ(convertState.nextIndex, 1);
+
+ intResult = fromString<int>(StringView{"-1"});
+ ASSERT_TRUE(intResult.isOkay());
+ convertState = intResult.getOkay();
+ ASSERT_EQ(convertState.value, -1);
+ ASSERT_EQ(convertState.nextIndex, 2);
+
+ intResult = fromString<int>(StringView{"+1"});
+ ASSERT_TRUE(intResult.isOkay());
+ convertState = intResult.getOkay();
+ ASSERT_EQ(convertState.value, 1);
+ ASSERT_EQ(convertState.nextIndex, 2);
+
+ intResult = fromString<int>(StringView{" +42"});
+ ASSERT_TRUE(intResult.isOkay());
+ convertState = intResult.getOkay();
+ ASSERT_EQ(convertState.value, 42);
+ ASSERT_EQ(convertState.nextIndex, 4);
+
+ intResult = fromString<int>(StringView{" +42Q"});
+ ASSERT_TRUE(intResult.isOkay());
+ convertState = intResult.getOkay();
+ ASSERT_EQ(convertState.value, 42);
+ ASSERT_EQ(convertState.nextIndex, 4);
+}
+
+TEST(FudStringConvert, ConvertUnsigned)
+{
+ auto unsignedResult = fromString<uint16_t>(StringView{"0"});
+ ASSERT_TRUE(unsignedResult.isOkay());
+ auto convertState = unsignedResult.getOkay();
+ ASSERT_EQ(convertState.value, 0);
+ ASSERT_EQ(convertState.nextIndex, 1);
+
+ unsignedResult = fromString<uint16_t>(StringView{"1"});
+ ASSERT_TRUE(unsignedResult.isOkay());
+ convertState = unsignedResult.getOkay();
+ ASSERT_EQ(convertState.value, 1);
+ ASSERT_EQ(convertState.nextIndex, 1);
+
+ unsignedResult = fromString<uint16_t>(StringView{"-1"});
+ ASSERT_TRUE(unsignedResult.isError());
+ ASSERT_EQ(unsignedResult.getError(), FudStatus::ArgumentInvalid);
+
+ unsignedResult = fromString<uint16_t>(StringView{"0xFFFF"});
+ ASSERT_TRUE(unsignedResult.isOkay());
+ convertState = unsignedResult.getOkay();
+ ASSERT_EQ(convertState.value, 0xFFFF);
+ ASSERT_EQ(convertState.nextIndex, 6);
+
+ unsignedResult = fromString<uint16_t>(StringView{"0x10000"});
+ ASSERT_TRUE(unsignedResult.isError());
+ ASSERT_EQ(unsignedResult.getError(), FudStatus::RangeError);
+
+ unsignedResult = fromString<uint16_t>(StringView{"+1"});
+ ASSERT_TRUE(unsignedResult.isOkay());
+ convertState = unsignedResult.getOkay();
+ ASSERT_EQ(convertState.value, 1);
+ ASSERT_EQ(convertState.nextIndex, 2);
+
+ unsignedResult = fromString<uint16_t>(StringView{" +42"});
+ ASSERT_TRUE(unsignedResult.isOkay());
+ convertState = unsignedResult.getOkay();
+ ASSERT_EQ(convertState.value, 42);
+ ASSERT_EQ(convertState.nextIndex, 4);
+
+ unsignedResult = fromString<uint16_t>(StringView{" +42Q"});
+ ASSERT_TRUE(unsignedResult.isOkay());
+ convertState = unsignedResult.getOkay();
+ ASSERT_EQ(convertState.value, 42);
+ ASSERT_EQ(convertState.nextIndex, 4);
+}
+
+TEST(FudStringConvert, ConvertFloat)
+{
+ auto floatResult = fromString<float>(StringView{"0"});
+ ASSERT_TRUE(floatResult.isOkay());
+ auto convertState = floatResult.getOkay();
+ ASSERT_EQ(convertState.value, 0.0);
+ ASSERT_EQ(convertState.nextIndex, 1);
+
+ floatResult = fromString<float>(StringView{"1.0"});
+ ASSERT_TRUE(floatResult.isOkay());
+ convertState = floatResult.getOkay();
+ ASSERT_EQ(convertState.value, 1.0);
+ ASSERT_EQ(convertState.nextIndex, 3);
+
+ floatResult = fromString<float>(StringView{"1.5"});
+ ASSERT_TRUE(floatResult.isOkay());
+ convertState = floatResult.getOkay();
+ ASSERT_EQ(convertState.value, 1.5);
+ ASSERT_EQ(convertState.nextIndex, 3);
+
+ floatResult = fromString<float>(StringView{"1.5 next"});
+ ASSERT_TRUE(floatResult.isOkay());
+ convertState = floatResult.getOkay();
+ ASSERT_EQ(convertState.value, 1.5);
+ ASSERT_EQ(convertState.nextIndex, 3);
+
+ floatResult = fromString<float>(StringView{"-1.0"});
+ ASSERT_TRUE(floatResult.isOkay());
+ convertState = floatResult.getOkay();
+ ASSERT_EQ(convertState.value, -1.0);
+ ASSERT_EQ(convertState.nextIndex, 4);
+
+ floatResult = fromString<float>(StringView{"-1.5 next"});
+ ASSERT_TRUE(floatResult.isOkay());
+ convertState = floatResult.getOkay();
+ ASSERT_EQ(convertState.value, -1.5);
+ ASSERT_EQ(convertState.nextIndex, 4);
+
+ floatResult = fromString<float>(StringView{" -1.5 next"});
+ ASSERT_TRUE(floatResult.isOkay());
+ convertState = floatResult.getOkay();
+ ASSERT_EQ(convertState.value, -1.5);
+ ASSERT_EQ(convertState.nextIndex, 5);
+
+ floatResult = fromString<float>(StringView{" -1.5E+3 next"});
+ ASSERT_TRUE(floatResult.isOkay());
+ convertState = floatResult.getOkay();
+ ASSERT_EQ(convertState.value, -1.5E+3);
+ ASSERT_EQ(convertState.nextIndex, sizeof(" -1.5E+3") - 1);
+
+ floatResult = fromString<float>(StringView{" -15E+2 next"});
+ ASSERT_TRUE(floatResult.isOkay());
+ convertState = floatResult.getOkay();
+ ASSERT_EQ(convertState.value, -1.5E+3);
+ ASSERT_EQ(convertState.nextIndex, sizeof(" -15E+2") - 1);
+
+ floatResult = fromString<float>(StringView{" -1EA next"});
+ ASSERT_TRUE(floatResult.isError());
+ ASSERT_EQ(floatResult.getError(), FudStatus::ArgumentInvalid);
+
+ floatResult = fromString<float>(StringView{" -1.5EA next"});
+ ASSERT_TRUE(floatResult.isError());
+ ASSERT_EQ(floatResult.getError(), FudStatus::ArgumentInvalid);
+
+ floatResult = fromString<float>(StringView{" -1.5EQ next"});
+ ASSERT_TRUE(floatResult.isError());
+ ASSERT_EQ(floatResult.getError(), FudStatus::ArgumentInvalid);
+
+ floatResult = fromString<float>(StringView{" -1.5E3Q next"});
+ ASSERT_TRUE(floatResult.isOkay());
+ convertState = floatResult.getOkay();
+ ASSERT_EQ(convertState.value, -1.5E3);
+ ASSERT_EQ(convertState.nextIndex, sizeof(" -1.5E3") - 1);
+}
+
+} // namespace fud
diff --git a/test/test_utf8.cpp b/test/test_utf8.cpp
index d1737f7..4b57b64 100644
--- a/test/test_utf8.cpp
+++ b/test/test_utf8.cpp
@@ -30,6 +30,35 @@
namespace fud {
+using classify::CharPredicate;
+using classify::FudUtf8Predicate;
+using classify::isAscii;
+using classify::isAscii;
+using classify:: isAlphanumeric;
+using classify:: isAlphanumeric;
+using classify:: isAlpha;
+using classify:: isAlpha;
+using classify:: isLowercase;
+using classify:: isLowercase;
+using classify:: isUppercase;
+using classify:: isUppercase;
+using classify:: isDigit;
+using classify:: isDigit;
+using classify:: isHexDigit;
+using classify:: isHexDigit;
+using classify:: isControl;
+using classify:: isControl;
+using classify:: isGraphical;
+using classify:: isGraphical;
+using classify:: isSpace;
+using classify:: isSpace;
+using classify:: isBlank;
+using classify:: isBlank;
+using classify:: isPrintable;
+using classify:: isPrintable;
+using classify:: isPunctuation;
+using classify:: isPunctuation;
+
constexpr size_t validAsciiSize = INT8_MAX + 1;
constexpr size_t invalidAsciiSize = UINT8_MAX + 1 - validAsciiSize;
@@ -194,24 +223,24 @@ TEST(Utf8Test, Utf8MultiByte)
TEST(Utf8Test, Utf8IsAscii)
{
- ASSERT_FALSE(charIsAscii(invalidAscii));
+ ASSERT_FALSE(isAscii(invalidAscii));
Iota<int16_t> charIota{0, 1, validAsciiSize};
- ASSERT_TRUE(allOf([&]() -> Option<char> { return charIota().map(toLetter<int16_t>); }, charIsAscii));
+ ASSERT_TRUE(allOf([&]() -> Option<char> { return charIota().map(toLetter<int16_t>); }, static_cast<CharPredicate>(isAscii)));
Iota<int16_t> invalidCharIota{validAsciiSize, 1, invalidAsciiSize};
- ASSERT_FALSE(anyOf([&]() -> Option<char> { return invalidCharIota().map(toLetter<int16_t>); }, charIsAscii));
+ ASSERT_FALSE(anyOf([&]() -> Option<char> { return invalidCharIota().map(toLetter<int16_t>); }, static_cast<CharPredicate>(isAscii)));
FudUtf8 unicode{FudUtf8::invalidAscii()};
- ASSERT_FALSE(utf8IsAscii(unicode));
+ ASSERT_FALSE(isAscii(unicode));
charIota.set(0);
- ASSERT_TRUE(allOf([&]() -> Option<FudUtf8> { return charIota().map(toUtf8<int16_t>); }, utf8IsAscii));
+ ASSERT_TRUE(allOf([&]() -> Option<FudUtf8> { return charIota().map(toUtf8<int16_t>); }, static_cast<FudUtf8Predicate>(isAscii)));
invalidCharIota.set(invalidAsciiSize);
- ASSERT_FALSE(anyOf([&]() -> Option<FudUtf8> { return invalidCharIota().map(toUtf8<int16_t>); }, utf8IsAscii));
+ ASSERT_FALSE(anyOf([&]() -> Option<FudUtf8> { return invalidCharIota().map(toUtf8<int16_t>); }, static_cast<FudUtf8Predicate>(isAscii)));
}
template <typename T, size_t Size = SIZE_MAX>
@@ -241,22 +270,22 @@ TEST(Utf8Test, Utf8IsAlphanumeric)
Array<char, numAlphanumericChars> alphanumericChars{};
copyMem<numAlphanumericChars>(alphanumericChars, alphanumericCharLiteral);
- ASSERT_TRUE(allOf(alphanumericChars.span(), charIsAlphanumeric));
+ ASSERT_TRUE(allOf(alphanumericChars.span(), static_cast<CharPredicate>(isAlphanumeric)));
constexpr size_t numNonAlphanumericChars = validAsciiSize - numAlphanumericChars;
Vector<char> nonAlphanumericChars{};
for (char idx = 0; idx < INT8_MAX; ++idx) {
- if (!charIsAlphanumeric(idx)) {
+ if (!isAlphanumeric(idx)) {
ASSERT_EQ(nonAlphanumericChars.pushBack(idx), FudStatus::Success);
}
}
auto nonAlphanumericSpan{nonAlphanumericChars.span().takeOkay()};
- ASSERT_FALSE(anyOf(nonAlphanumericSpan, charIsAlphanumeric));
+ ASSERT_FALSE(anyOf(nonAlphanumericSpan, static_cast<CharPredicate>(isAlphanumeric)));
auto invalidAsciiChars = invalidAsciiGenerator();
- ASSERT_FALSE(anyOf(invalidAsciiChars, charIsAlphanumeric));
+ ASSERT_FALSE(anyOf(invalidAsciiChars, static_cast<CharPredicate>(isAlphanumeric)));
- ASSERT_FALSE(utf8IsAlphanumeric(FudUtf8{Ascii{invalidAscii}}));
+ ASSERT_FALSE(isAlphanumeric(FudUtf8{Ascii{invalidAscii}}));
auto iotaGenerator = invalidAsciiGenerator();
auto generator = generate(
@@ -265,11 +294,11 @@ TEST(Utf8Test, Utf8IsAlphanumeric)
SpanGenerator<char, alphanumericChars.size()> alphanumericGenerator{alphanumericChars.span()};
auto utf8AlphanumericGenerator = [&]() { return alphanumericGenerator().map(toUtf8<uint16_t>); };
- ASSERT_TRUE(allOf(utf8AlphanumericGenerator, utf8IsAlphanumeric));
+ ASSERT_TRUE(allOf(utf8AlphanumericGenerator, static_cast<FudUtf8Predicate>(isAlphanumeric)));
SpanGenerator<char> nonAlphanumericGenerator{nonAlphanumericChars.span().takeOkay()};
auto utf8NonAlphanumericGenerator = [&]() { return nonAlphanumericGenerator().map(toUtf8<uint16_t>); };
- ASSERT_FALSE(anyOf(utf8NonAlphanumericGenerator, utf8IsAlphanumeric));
+ ASSERT_FALSE(anyOf(utf8NonAlphanumericGenerator, static_cast<FudUtf8Predicate>(isAlphanumeric)));
}
TEST(Utf8Test, Utf8IsAlpha)
@@ -279,29 +308,29 @@ TEST(Utf8Test, Utf8IsAlpha)
Array<char, numAlphaChars> alphaChars{};
copyMem<numAlphaChars>(alphaChars, alphaCharLiteral);
- ASSERT_TRUE(allOf(alphaChars.span(), charIsAlpha));
+ ASSERT_TRUE(allOf(alphaChars.span(), static_cast<CharPredicate>(isAlpha)));
constexpr size_t numNonAlphanumericChars = validAsciiSize - numAlphaChars;
Vector<char> nonAlphaChars{};
for (char idx = 0; idx < INT8_MAX; ++idx) {
- if (!charIsAlphanumeric(idx)) {
+ if (!isAlphanumeric(idx)) {
ASSERT_EQ(nonAlphaChars.pushBack(idx), FudStatus::Success);
}
}
- ASSERT_FALSE(anyOf(nonAlphaChars.span().takeOkay(), charIsAlpha));
+ ASSERT_FALSE(anyOf(nonAlphaChars.span().takeOkay(), static_cast<CharPredicate>(isAlpha)));
auto invalidAsciiChars = invalidAsciiGenerator();
- ASSERT_FALSE(anyOf(invalidAsciiChars, charIsAlpha));
+ ASSERT_FALSE(anyOf(invalidAsciiChars, static_cast<CharPredicate>(isAlpha)));
- ASSERT_FALSE(utf8IsAlpha(FudUtf8{Ascii{invalidAscii}}));
+ ASSERT_FALSE(isAlpha(FudUtf8{Ascii{invalidAscii}}));
SpanGenerator<char, alphaChars.size()> alphaGenerator{alphaChars.span()};
auto utf8AlphaGenerator = [&]() { return alphaGenerator().map(toUtf8<uint16_t>); };
- ASSERT_TRUE(allOf(utf8AlphaGenerator, utf8IsAlpha));
+ ASSERT_TRUE(allOf(utf8AlphaGenerator, static_cast<FudUtf8Predicate>(isAlpha)));
SpanGenerator<char> nonAlphaGenerator{nonAlphaChars.span().takeOkay()};
auto utf8NonAlphaGenerator = [&]() { return nonAlphaGenerator().map(toUtf8<uint16_t>); };
- ASSERT_FALSE(anyOf(utf8NonAlphaGenerator, utf8IsAlpha));
+ ASSERT_FALSE(anyOf(utf8NonAlphaGenerator, static_cast<FudUtf8Predicate>(isAlpha)));
}
TEST(Utf8Test, Utf8IsLower)
@@ -311,29 +340,29 @@ TEST(Utf8Test, Utf8IsLower)
Array<char, numLowerChars> lowerChars{};
copyMem<numLowerChars>(lowerChars, lowerCharLiteral);
- ASSERT_TRUE(allOf(lowerChars.span(), charIsLowercase));
+ ASSERT_TRUE(allOf(lowerChars.span(), static_cast<CharPredicate>(isLowercase)));
constexpr size_t numNonLowerChars = validAsciiSize - numLowerChars;
Vector<char> nonLowerChars{};
for (char idx = 0; idx < INT8_MAX; ++idx) {
- if (!charIsLowercase(idx)) {
+ if (!isLowercase(idx)) {
ASSERT_EQ(nonLowerChars.pushBack(idx), FudStatus::Success);
}
}
- ASSERT_FALSE(anyOf(nonLowerChars.span().takeOkay(), charIsLowercase));
+ ASSERT_FALSE(anyOf(nonLowerChars.span().takeOkay(), static_cast<CharPredicate>(isLowercase)));
auto invalidAsciiChars = invalidAsciiGenerator();
- ASSERT_FALSE(anyOf(invalidAsciiChars, charIsLowercase));
+ ASSERT_FALSE(anyOf(invalidAsciiChars, static_cast<CharPredicate>(isLowercase)));
- ASSERT_FALSE(utf8IsLowercase(FudUtf8{Ascii{invalidAscii}}));
+ ASSERT_FALSE(isLowercase(FudUtf8{Ascii{invalidAscii}}));
SpanGenerator<char, lowerChars.size()> lowerGenerator{lowerChars.span()};
auto utf8LowerGenerator = [&]() { return lowerGenerator().map(toUtf8<uint16_t>); };
- ASSERT_TRUE(allOf(utf8LowerGenerator, utf8IsLowercase));
+ ASSERT_TRUE(allOf(utf8LowerGenerator, static_cast<FudUtf8Predicate>(isLowercase)));
SpanGenerator<char> nonLowerGenerator{nonLowerChars.span().takeOkay()};
auto utf8NonLowerGenerator = [&]() { return nonLowerGenerator().map(toUtf8<uint16_t>); };
- ASSERT_FALSE(anyOf(utf8NonLowerGenerator, utf8IsLowercase));
+ ASSERT_FALSE(anyOf(utf8NonLowerGenerator, static_cast<FudUtf8Predicate>(isLowercase)));
}
TEST(Utf8Test, Utf8IsUpper)
@@ -343,29 +372,29 @@ TEST(Utf8Test, Utf8IsUpper)
Array<char, numUpperChars> upperChars{};
copyMem<numUpperChars>(upperChars, upperCharLiteral);
- ASSERT_TRUE(allOf(upperChars.span(), charIsUppercase));
+ ASSERT_TRUE(allOf(upperChars.span(), static_cast<CharPredicate>(isUppercase)));
constexpr size_t numNonUpperChars = validAsciiSize - numUpperChars;
Vector<char> nonUpperChars{};
for (char idx = 0; idx < INT8_MAX; ++idx) {
- if (!charIsUppercase(idx)) {
+ if (!isUppercase(idx)) {
ASSERT_EQ(nonUpperChars.pushBack(idx), FudStatus::Success);
}
}
- ASSERT_FALSE(anyOf(nonUpperChars.span().takeOkay(), charIsUppercase));
+ ASSERT_FALSE(anyOf(nonUpperChars.span().takeOkay(), static_cast<CharPredicate>(isUppercase)));
auto invalidAsciiChars = invalidAsciiGenerator();
- ASSERT_FALSE(anyOf(invalidAsciiChars, charIsUppercase));
+ ASSERT_FALSE(anyOf(invalidAsciiChars, static_cast<CharPredicate>(isUppercase)));
- ASSERT_FALSE(utf8IsUppercase(FudUtf8{Ascii{invalidAscii}}));
+ ASSERT_FALSE(isUppercase(FudUtf8{Ascii{invalidAscii}}));
SpanGenerator<char, upperChars.size()> upperGenerator{upperChars.span()};
auto utf8UpperGenerator = [&]() { return upperGenerator().map(toUtf8<uint16_t>); };
- ASSERT_TRUE(allOf(utf8UpperGenerator, utf8IsUppercase));
+ ASSERT_TRUE(allOf(utf8UpperGenerator, static_cast<FudUtf8Predicate>(isUppercase)));
SpanGenerator<char> nonUpperGenerator{nonUpperChars.span().takeOkay()};
auto utf8NonUpperGenerator = [&]() { return nonUpperGenerator().map(toUtf8<uint16_t>); };
- ASSERT_FALSE(anyOf(utf8NonUpperGenerator, utf8IsUppercase));
+ ASSERT_FALSE(anyOf(utf8NonUpperGenerator, static_cast<FudUtf8Predicate>(isUppercase)));
}
TEST(Utf8Test, Utf8IsDigit)
@@ -375,29 +404,29 @@ TEST(Utf8Test, Utf8IsDigit)
Array<char, numDigitChars> digitChars{};
copyMem<numDigitChars>(digitChars, digitCharLiteral);
- ASSERT_TRUE(allOf(digitChars.span(), charIsDigit));
+ ASSERT_TRUE(allOf(digitChars.span(), static_cast<CharPredicate>(isDigit)));
constexpr size_t numNonDigitChars = validAsciiSize - numDigitChars;
Vector<char> nonDigitChars{};
for (char idx = 0; idx < INT8_MAX; ++idx) {
- if (!charIsDigit(idx)) {
+ if (!isDigit(idx)) {
ASSERT_EQ(nonDigitChars.pushBack(idx), FudStatus::Success);
}
}
- ASSERT_FALSE(anyOf(nonDigitChars.span().takeOkay(), charIsDigit));
+ ASSERT_FALSE(anyOf(nonDigitChars.span().takeOkay(), static_cast<CharPredicate>(isDigit)));
auto invalidAsciiChars = invalidAsciiGenerator();
- ASSERT_FALSE(anyOf(invalidAsciiChars, charIsDigit));
+ ASSERT_FALSE(anyOf(invalidAsciiChars, static_cast<CharPredicate>(isDigit)));
- ASSERT_FALSE(utf8IsDigit(FudUtf8{Ascii{invalidAscii}}));
+ ASSERT_FALSE(isDigit(FudUtf8{Ascii{invalidAscii}}));
SpanGenerator<char, digitChars.size()> digitGenerator{digitChars.span()};
auto utf8DigitGenerator = [&]() { return digitGenerator().map(toUtf8<uint16_t>); };
- ASSERT_TRUE(allOf(utf8DigitGenerator, utf8IsDigit));
+ ASSERT_TRUE(allOf(utf8DigitGenerator, static_cast<FudUtf8Predicate>(isDigit)));
SpanGenerator<char> nonDigitGenerator{nonDigitChars.span().takeOkay()};
auto utf8NonDigitGenerator = [&]() { return nonDigitGenerator().map(toUtf8<uint16_t>); };
- ASSERT_FALSE(anyOf(utf8NonDigitGenerator, utf8IsDigit));
+ ASSERT_FALSE(anyOf(utf8NonDigitGenerator, static_cast<FudUtf8Predicate>(isDigit)));
}
TEST(Utf8Test, Utf8IsHexDigit)
@@ -407,29 +436,29 @@ TEST(Utf8Test, Utf8IsHexDigit)
Array<char, numHexDigitChars> hexDigitChars{};
copyMem<numHexDigitChars>(hexDigitChars, hexDigitCharLiteral);
- ASSERT_TRUE(allOf(hexDigitChars.span(), charIsHexDigit));
+ ASSERT_TRUE(allOf(hexDigitChars.span(), static_cast<CharPredicate>(isHexDigit)));
constexpr size_t numNonHexDigitChars = validAsciiSize - numHexDigitChars;
Vector<char> nonHexDigitChars{};
for (char idx = 0; idx < INT8_MAX; ++idx) {
- if (!charIsHexDigit(idx)) {
+ if (!isHexDigit(idx)) {
ASSERT_EQ(nonHexDigitChars.pushBack(idx), FudStatus::Success);
}
}
- ASSERT_FALSE(anyOf(nonHexDigitChars.span().takeOkay(), charIsHexDigit));
+ ASSERT_FALSE(anyOf(nonHexDigitChars.span().takeOkay(), static_cast<CharPredicate>(isHexDigit)));
auto invalidAsciiChars = invalidAsciiGenerator();
- ASSERT_FALSE(anyOf(invalidAsciiChars, charIsHexDigit));
+ ASSERT_FALSE(anyOf(invalidAsciiChars, static_cast<CharPredicate>(isHexDigit)));
- ASSERT_FALSE(utf8IsHexDigit(FudUtf8{Ascii{invalidAscii}}));
+ ASSERT_FALSE(isHexDigit(FudUtf8{Ascii{invalidAscii}}));
SpanGenerator<char, hexDigitChars.size()> hexDigitGenerator{hexDigitChars.span()};
auto utf8HexDigitGenerator = [&]() { return hexDigitGenerator().map(toUtf8<uint16_t>); };
- ASSERT_TRUE(allOf(utf8HexDigitGenerator, utf8IsHexDigit));
+ ASSERT_TRUE(allOf(utf8HexDigitGenerator, static_cast<FudUtf8Predicate>(isHexDigit)));
SpanGenerator<char> nonHexDigitGenerator{nonHexDigitChars.span().takeOkay()};
auto utf8NonHexDigitGenerator = [&]() { return nonHexDigitGenerator().map(toUtf8<uint16_t>); };
- ASSERT_FALSE(anyOf(utf8NonHexDigitGenerator, utf8IsHexDigit));
+ ASSERT_FALSE(anyOf(utf8NonHexDigitGenerator, static_cast<FudUtf8Predicate>(isHexDigit)));
}
TEST(Utf8Test, Utf8IsControl)
@@ -439,7 +468,7 @@ TEST(Utf8Test, Utf8IsControl)
constexpr const char deleteChar = 0x7F;
controlChars.back() = deleteChar;
- ASSERT_TRUE(allOf(controlChars.span(), charIsControl));
+ ASSERT_TRUE(allOf(controlChars.span(), static_cast<CharPredicate>(isControl)));
constexpr size_t numNonControlChars = INT8_MAX + 1 - numControlChars;
Vector<char> nonControlChars{};
@@ -447,21 +476,21 @@ TEST(Utf8Test, Utf8IsControl)
for (auto idx = numControlChars - 1; idx < deleteChar; ++idx) {
ASSERT_EQ(nonControlChars.pushBack(idx), FudStatus::Success);
}
- ASSERT_FALSE(anyOf(nonControlChars.span().takeOkay(), charIsControl));
- ASSERT_TRUE(allOf(nonControlChars.span().takeOkay(), charIsAscii));
+ ASSERT_FALSE(anyOf(nonControlChars.span().takeOkay(), static_cast<CharPredicate>(isControl)));
+ ASSERT_TRUE(allOf(nonControlChars.span().takeOkay(), static_cast<CharPredicate>(isAscii)));
auto invalidAsciiChars = invalidAsciiGenerator();
- ASSERT_FALSE(anyOf(invalidAsciiChars, charIsControl));
+ ASSERT_FALSE(anyOf(invalidAsciiChars, static_cast<CharPredicate>(isControl)));
- ASSERT_FALSE(utf8IsControl(FudUtf8{Ascii{invalidAscii}}));
+ ASSERT_FALSE(isControl(FudUtf8{Ascii{invalidAscii}}));
SpanGenerator<char, controlChars.size()> controlGenerator{controlChars.span()};
auto utf8ControlGenerator = [&]() { return controlGenerator().map(toUtf8<uint16_t>); };
- ASSERT_TRUE(allOf(utf8ControlGenerator, utf8IsControl));
+ ASSERT_TRUE(allOf(utf8ControlGenerator, static_cast<FudUtf8Predicate>(isControl)));
SpanGenerator<char> nonControlGenerator{nonControlChars.span().takeOkay()};
auto utf8NonControlGenerator = [&]() { return nonControlGenerator().map(toUtf8<uint16_t>); };
- ASSERT_FALSE(anyOf(utf8NonControlGenerator, utf8IsControl));
+ ASSERT_FALSE(anyOf(utf8NonControlGenerator, static_cast<FudUtf8Predicate>(isControl)));
}
TEST(Utf8Test, Utf8IsGraphical)
@@ -471,32 +500,32 @@ TEST(Utf8Test, Utf8IsGraphical)
Array<char, numGraphicalChars> graphicalChars{};
copyMem<numGraphicalChars>(graphicalChars, graphicalCharLiteral);
- ASSERT_TRUE(allOf(graphicalChars.span(), charIsGraphical));
+ ASSERT_TRUE(allOf(graphicalChars.span(), static_cast<CharPredicate>(isGraphical)));
constexpr size_t numNonGraphicalChars = validAsciiSize - numGraphicalChars;
Vector<char> nonGraphicalChars{};
ASSERT_EQ(nonGraphicalChars.reserve(numNonGraphicalChars), FudStatus::Success);
for (uint8_t idx = 0; idx < INT8_MAX + 1; ++idx) {
- if (!charIsGraphical(static_cast<char>(idx))) {
+ if (!isGraphical(static_cast<char>(idx))) {
ASSERT_EQ(nonGraphicalChars.pushBack(static_cast<char>(idx)), FudStatus::Success);
}
}
- ASSERT_FALSE(anyOf(nonGraphicalChars.span().takeOkay(), charIsGraphical));
- ASSERT_TRUE(allOf(nonGraphicalChars.span().takeOkay(), charIsAscii));
+ ASSERT_FALSE(anyOf(nonGraphicalChars.span().takeOkay(), static_cast<CharPredicate>(isGraphical)));
+ ASSERT_TRUE(allOf(nonGraphicalChars.span().takeOkay(), static_cast<CharPredicate>(isAscii)));
ASSERT_EQ(nonGraphicalChars.size() + graphicalChars.size(), INT8_MAX + 1);
auto invalidAsciiChars = invalidAsciiGenerator();
- ASSERT_FALSE(anyOf(invalidAsciiChars, charIsGraphical));
+ ASSERT_FALSE(anyOf(invalidAsciiChars, static_cast<CharPredicate>(isGraphical)));
- ASSERT_FALSE(utf8IsGraphical(FudUtf8{Ascii{invalidAscii}}));
+ ASSERT_FALSE(isGraphical(FudUtf8{Ascii{invalidAscii}}));
SpanGenerator<char, graphicalChars.size()> graphicalGenerator{graphicalChars.span()};
auto utf8GraphicalGenerator = [&]() { return graphicalGenerator().map(toUtf8<uint16_t>); };
- ASSERT_TRUE(allOf(utf8GraphicalGenerator, utf8IsGraphical));
+ ASSERT_TRUE(allOf(utf8GraphicalGenerator, static_cast<FudUtf8Predicate>(isGraphical)));
SpanGenerator<char> nonGraphicalGenerator{nonGraphicalChars.span().takeOkay()};
auto utf8NonGraphicalGenerator = [&]() { return nonGraphicalGenerator().map(toUtf8<uint16_t>); };
- ASSERT_FALSE(anyOf(utf8NonGraphicalGenerator, utf8IsGraphical));
+ ASSERT_FALSE(anyOf(utf8NonGraphicalGenerator, static_cast<FudUtf8Predicate>(isGraphical)));
}
TEST(Utf8Test, Utf8IsSpace)
@@ -506,32 +535,32 @@ TEST(Utf8Test, Utf8IsSpace)
Array<char, numSpaceChars> spaceChars{};
copyMem<numSpaceChars>(spaceChars, spaceCharLiteral);
- ASSERT_TRUE(allOf(spaceChars.span(), charIsSpace));
+ ASSERT_TRUE(allOf(spaceChars.span(), static_cast<CharPredicate>(isSpace)));
constexpr size_t numNonSpaceChars = validAsciiSize - numSpaceChars;
Vector<char> nonSpaceChars{};
ASSERT_EQ(nonSpaceChars.reserve(numNonSpaceChars), FudStatus::Success);
for (uint8_t idx = 0; idx < INT8_MAX + 1; ++idx) {
- if (!charIsSpace(static_cast<char>(idx))) {
+ if (!isSpace(static_cast<char>(idx))) {
ASSERT_EQ(nonSpaceChars.pushBack(static_cast<char>(idx)), FudStatus::Success);
}
}
- ASSERT_FALSE(anyOf(nonSpaceChars.span().takeOkay(), charIsSpace));
- ASSERT_TRUE(allOf(nonSpaceChars.span().takeOkay(), charIsAscii));
+ ASSERT_FALSE(anyOf(nonSpaceChars.span().takeOkay(), static_cast<CharPredicate>(isSpace)));
+ ASSERT_TRUE(allOf(nonSpaceChars.span().takeOkay(), static_cast<CharPredicate>(isAscii)));
ASSERT_EQ(nonSpaceChars.size() + spaceChars.size(), INT8_MAX + 1);
auto invalidAsciiChars = invalidAsciiGenerator();
- ASSERT_FALSE(anyOf(invalidAsciiChars, charIsSpace));
+ ASSERT_FALSE(anyOf(invalidAsciiChars, static_cast<CharPredicate>(isSpace)));
- ASSERT_FALSE(utf8IsSpace(FudUtf8{Ascii{invalidAscii}}));
+ ASSERT_FALSE(isSpace(FudUtf8{Ascii{invalidAscii}}));
SpanGenerator<char, spaceChars.size()> spaceGenerator{spaceChars.span()};
auto utf8SpaceGenerator = [&]() { return spaceGenerator().map(toUtf8<uint16_t>); };
- ASSERT_TRUE(allOf(utf8SpaceGenerator, utf8IsSpace));
+ ASSERT_TRUE(allOf(utf8SpaceGenerator, static_cast<FudUtf8Predicate>(isSpace)));
SpanGenerator<char> nonSpaceGenerator{nonSpaceChars.span().takeOkay()};
auto utf8NonSpaceGenerator = [&]() { return nonSpaceGenerator().map(toUtf8<uint16_t>); };
- ASSERT_FALSE(anyOf(utf8NonSpaceGenerator, utf8IsSpace));
+ ASSERT_FALSE(anyOf(utf8NonSpaceGenerator, static_cast<FudUtf8Predicate>(isSpace)));
}
TEST(Utf8Test, Utf8IsBlank)
@@ -541,32 +570,32 @@ TEST(Utf8Test, Utf8IsBlank)
Array<char, numBlankChars> blankChars{};
copyMem<numBlankChars>(blankChars, blankCharLiteral);
- ASSERT_TRUE(allOf(blankChars.span(), charIsBlank));
+ ASSERT_TRUE(allOf(blankChars.span(), static_cast<CharPredicate>(isBlank)));
constexpr size_t numNonBlankChars = validAsciiSize - numBlankChars;
Vector<char> nonBlankChars{};
ASSERT_EQ(nonBlankChars.reserve(numNonBlankChars), FudStatus::Success);
for (uint8_t idx = 0; idx < INT8_MAX + 1; ++idx) {
- if (!charIsBlank(static_cast<char>(idx))) {
+ if (!isBlank(static_cast<char>(idx))) {
ASSERT_EQ(nonBlankChars.pushBack(static_cast<char>(idx)), FudStatus::Success);
}
}
- ASSERT_FALSE(anyOf(nonBlankChars.span().takeOkay(), charIsBlank));
- ASSERT_TRUE(allOf(nonBlankChars.span().takeOkay(), charIsAscii));
+ ASSERT_FALSE(anyOf(nonBlankChars.span().takeOkay(), static_cast<CharPredicate>(isBlank)));
+ ASSERT_TRUE(allOf(nonBlankChars.span().takeOkay(), static_cast<CharPredicate>(isAscii)));
ASSERT_EQ(nonBlankChars.size() + blankChars.size(), INT8_MAX + 1);
auto invalidAsciiChars = invalidAsciiGenerator();
- ASSERT_FALSE(anyOf(invalidAsciiChars, charIsBlank));
+ ASSERT_FALSE(anyOf(invalidAsciiChars, static_cast<CharPredicate>(isBlank)));
- ASSERT_FALSE(utf8IsBlank(FudUtf8{Ascii{invalidAscii}}));
+ ASSERT_FALSE(isBlank(FudUtf8{Ascii{invalidAscii}}));
SpanGenerator<char, blankChars.size()> blankGenerator{blankChars.span()};
auto utf8BlankGenerator = [&]() { return blankGenerator().map(toUtf8<uint16_t>); };
- ASSERT_TRUE(allOf(utf8BlankGenerator, utf8IsBlank));
+ ASSERT_TRUE(allOf(utf8BlankGenerator, static_cast<FudUtf8Predicate>(isBlank)));
SpanGenerator<char> nonBlankGenerator{nonBlankChars.span().takeOkay()};
auto utf8NonBlankGenerator = [&]() { return nonBlankGenerator().map(toUtf8<uint16_t>); };
- ASSERT_FALSE(anyOf(utf8NonBlankGenerator, utf8IsBlank));
+ ASSERT_FALSE(anyOf(utf8NonBlankGenerator, static_cast<FudUtf8Predicate>(isBlank)));
}
TEST(Utf8Test, Utf8IsPrintable)
@@ -576,32 +605,32 @@ TEST(Utf8Test, Utf8IsPrintable)
Array<char, numPrintableChars> printableChars{};
copyMem<numPrintableChars>(printableChars, printableCharLiteral);
- ASSERT_TRUE(allOf(printableChars.span(), charIsPrintable));
+ ASSERT_TRUE(allOf(printableChars.span(), static_cast<CharPredicate>(isPrintable)));
constexpr size_t numNonPrintableChars = validAsciiSize - numPrintableChars;
Vector<char> nonPrintableChars{};
ASSERT_EQ(nonPrintableChars.reserve(numNonPrintableChars), FudStatus::Success);
for (uint8_t idx = 0; idx < INT8_MAX + 1; ++idx) {
- if (!charIsPrintable(static_cast<char>(idx))) {
+ if (!isPrintable(static_cast<char>(idx))) {
ASSERT_EQ(nonPrintableChars.pushBack(static_cast<char>(idx)), FudStatus::Success);
}
}
- ASSERT_FALSE(anyOf(nonPrintableChars.span().takeOkay(), charIsPrintable));
- ASSERT_TRUE(allOf(nonPrintableChars.span().takeOkay(), charIsAscii));
+ ASSERT_FALSE(anyOf(nonPrintableChars.span().takeOkay(), static_cast<CharPredicate>(isPrintable)));
+ ASSERT_TRUE(allOf(nonPrintableChars.span().takeOkay(), static_cast<CharPredicate>(isAscii)));
ASSERT_EQ(nonPrintableChars.size() + printableChars.size(), INT8_MAX + 1);
auto invalidAsciiChars = invalidAsciiGenerator();
- ASSERT_FALSE(anyOf(invalidAsciiChars, charIsPrintable));
+ ASSERT_FALSE(anyOf(invalidAsciiChars, static_cast<CharPredicate>(isPrintable)));
- ASSERT_FALSE(utf8IsPrintable(FudUtf8{Ascii{invalidAscii}}));
+ ASSERT_FALSE(isPrintable(FudUtf8{Ascii{invalidAscii}}));
SpanGenerator<char, printableChars.size()> printableGenerator{printableChars.span()};
auto utf8PrintableGenerator = [&]() { return printableGenerator().map(toUtf8<uint16_t>); };
- ASSERT_TRUE(allOf(utf8PrintableGenerator, utf8IsPrintable));
+ ASSERT_TRUE(allOf(utf8PrintableGenerator, static_cast<FudUtf8Predicate>(isPrintable)));
SpanGenerator<char> nonPrintableGenerator{nonPrintableChars.span().takeOkay()};
auto utf8NonPrintableGenerator = [&]() { return nonPrintableGenerator().map(toUtf8<uint16_t>); };
- ASSERT_FALSE(anyOf(utf8NonPrintableGenerator, utf8IsPrintable));
+ ASSERT_FALSE(anyOf(utf8NonPrintableGenerator, static_cast<FudUtf8Predicate>(isPrintable)));
}
TEST(Utf8Test, Utf8IsPunctuation)
@@ -611,32 +640,32 @@ TEST(Utf8Test, Utf8IsPunctuation)
Array<char, numPunctuationChars> punctuationChars{};
copyMem<numPunctuationChars>(punctuationChars, punctuationCharLiteral);
- ASSERT_TRUE(allOf(punctuationChars.span(), charIsPunctuation));
+ ASSERT_TRUE(allOf(punctuationChars.span(), static_cast<CharPredicate>(isPunctuation)));
constexpr size_t numNonPunctuationChars = validAsciiSize - numPunctuationChars;
Vector<char> nonPunctuationChars{};
ASSERT_EQ(nonPunctuationChars.reserve(numNonPunctuationChars), FudStatus::Success);
for (uint8_t idx = 0; idx < INT8_MAX + 1; ++idx) {
- if (!charIsPunctuation(static_cast<char>(idx))) {
+ if (!isPunctuation(static_cast<char>(idx))) {
ASSERT_EQ(nonPunctuationChars.pushBack(static_cast<char>(idx)), FudStatus::Success);
}
}
- ASSERT_FALSE(anyOf(nonPunctuationChars.span().takeOkay(), charIsPunctuation));
- ASSERT_TRUE(allOf(nonPunctuationChars.span().takeOkay(), charIsAscii));
+ ASSERT_FALSE(anyOf(nonPunctuationChars.span().takeOkay(), static_cast<CharPredicate>(isPunctuation)));
+ ASSERT_TRUE(allOf(nonPunctuationChars.span().takeOkay(), static_cast<CharPredicate>(isAscii)));
ASSERT_EQ(nonPunctuationChars.size() + punctuationChars.size(), INT8_MAX + 1);
auto invalidAsciiChars = invalidAsciiGenerator();
- ASSERT_FALSE(anyOf(invalidAsciiChars, charIsPunctuation));
+ ASSERT_FALSE(anyOf(invalidAsciiChars, static_cast<CharPredicate>(isPunctuation)));
- ASSERT_FALSE(utf8IsPunctuation(FudUtf8{Ascii{invalidAscii}}));
+ ASSERT_FALSE(isPunctuation(FudUtf8{Ascii{invalidAscii}}));
SpanGenerator<char, punctuationChars.size()> punctuationGenerator{punctuationChars.span()};
auto utf8PunctuationGenerator = [&]() { return punctuationGenerator().map(toUtf8<uint16_t>); };
- ASSERT_TRUE(allOf(utf8PunctuationGenerator, utf8IsPunctuation));
+ ASSERT_TRUE(allOf(utf8PunctuationGenerator, static_cast<FudUtf8Predicate>(isPunctuation)));
SpanGenerator<char> nonPunctuationGenerator{nonPunctuationChars.span().takeOkay()};
auto utf8NonPunctuationGenerator = [&]() { return nonPunctuationGenerator().map(toUtf8<uint16_t>); };
- ASSERT_FALSE(anyOf(utf8NonPunctuationGenerator, utf8IsPunctuation));
+ ASSERT_FALSE(anyOf(utf8NonPunctuationGenerator, static_cast<FudUtf8Predicate>(isPunctuation)));
}
} // namespace fud
diff --git a/tools/coverage.sh b/tools/coverage.sh
index 95eef03..b41c66e 100755
--- a/tools/coverage.sh
+++ b/tools/coverage.sh
@@ -1,4 +1,5 @@
#!/bin/sh
+set -e
PROJ_ROOT=$(git rev-parse --show-toplevel)
cd $PROJ_ROOT
diff --git a/tools/create-pyenv.sh b/tools/create-pyenv.sh
index 64b1fbf..2c6d9d0 100755
--- a/tools/create-pyenv.sh
+++ b/tools/create-pyenv.sh
@@ -1,4 +1,5 @@
#!/bin/sh
+set -e
PROJ_ROOT=$(git rev-parse --show-toplevel)
cd $PROJ_ROOT
diff --git a/tools/run-cppcheck.sh b/tools/run-cppcheck.sh
index 1f8ee7d..315eff4 100755
--- a/tools/run-cppcheck.sh
+++ b/tools/run-cppcheck.sh
@@ -14,6 +14,7 @@
# 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.
+set -e
PROJ_ROOT=$(git rev-parse --show-toplevel)
cd $PROJ_ROOT