diff options
author | Dominick Allen <djallen@librehumanitas.org> | 2024-10-23 13:21:10 -0500 |
---|---|---|
committer | Dominick Allen <djallen@librehumanitas.org> | 2024-10-23 13:21:10 -0500 |
commit | 5cc7cbc3704ec255eb5d0ac53b2cc0fcb1221d63 (patch) | |
tree | 169d4d2d8dffe014851712e31a55036deb0c7c0c /source | |
parent | b2dbcb55e2832c373fecb4033a3ed77e5dbc77aa (diff) |
String conversion and parsing format spec.
Diffstat (limited to 'source')
-rw-r--r-- | source/fud_assert.cpp | 1 | ||||
-rw-r--r-- | source/fud_format.cpp | 515 | ||||
-rw-r--r-- | source/fud_string.cpp | 2 | ||||
-rw-r--r-- | source/fud_string_convert.cpp | 110 | ||||
-rw-r--r-- | source/fud_string_view.cpp | 701 | ||||
-rw-r--r-- | source/fud_utf8.cpp | 207 |
6 files changed, 775 insertions, 761 deletions
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; } |