#include "fud_format.hpp" #include "fud_string_convert.hpp" #include "fud_utf8.hpp" namespace fud { namespace impl { Result 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::parse(StringView formatView, size_t& specLength) { using RetType = Result; if (formatView.length() < 2 || formatView[0] != FormatSpec::openBracket) { return RetType::error(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 RetType::error(lengthResult.takeError()); } length += lengthResult.takeOkay(); if (length < 1) { return RetType::error(FudStatus::FormatInvalid); } specLength = length; auto status = impl::getPosition(formatView, spec, hasPosition); if (status != FudStatus::Success) { return RetType::error(status); } // check early ending if (!hasColon) { if (hasEnded(formatView)) { return RetType::okay(spec); } return RetType::error(FudStatus::FormatInvalid); } // check validity for being non-default spec and advance if (formatView[0] != ':') { return RetType::error(FudStatus::FormatInvalid); } formatView.advanceUnsafe(); if (hasEnded(formatView)) { return RetType::okay(spec); } // Spec status = impl::getFill(formatView, spec); if (status != FudStatus::Success) { return RetType::error(status); } if (hasEnded(formatView)) { return RetType::okay(spec); } // sign, alternate, leading zero status = impl::getSpecialOpts(formatView, spec); if (status != FudStatus::Success) { return RetType::error(status); } if (hasEnded(formatView)) { return RetType::okay(spec); } // Width status = impl::getWidth(formatView, spec); if (status != FudStatus::Success) { return RetType::error(status); } if (hasEnded(formatView)) { return RetType::okay(spec); } // Precision status = impl::getPrecision(formatView, spec); if (status != FudStatus::Success) { return RetType::error(status); } if (hasEnded(formatView)) { return RetType::okay(spec); } // Locale if (formatView[0] == FormatSpec::localeChar) { spec.hasLocale = true; formatView.advanceUnsafe(); } if (hasEnded(formatView)) { return RetType::okay(spec); } // Format Sign status = impl::getFormatSign(formatView, spec); if (status != FudStatus::Success) { return RetType::error(status); } if (!hasEnded(formatView)) { return RetType::error(FudStatus::FormatInvalid); } return RetType::okay(spec); } namespace impl { Result preScanSpec(StringView formatView, FormatSpec& spec, bool& hasPosition, bool& hasColon) { using RetType = Result; 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(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 RetType::error(FudStatus::FormatInvalid); } if (takesWidth) { spec.takesWidth = true; } else if (takesPrecision) { spec.takesPrecision = true; } else { return RetType::error(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 RetType::error(FudStatus::FormatInvalid); } index++; } if (nesting != 0 || captureGroups > 2 || index >= formatView.length() || formatView[index] != FormatSpec::closeBracket) { return RetType::error(FudStatus::FormatInvalid); } return RetType::okay(index + 1U); } FudStatus getPosition(StringView& formatView, FormatSpec& spec, bool& hasPosition) { if (hasPosition) { auto positionResult{fromString(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 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(FormatSign::Plus): spec.formatSign = FormatSign::Plus; break; case static_cast(FormatSign::Minus): spec.formatSign = FormatSign::Minus; break; case static_cast(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(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(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(formatView, Radix::Decimal)}; if (precisionResult.isError()) { return precisionResult.takeError(); } auto precisionValue{precisionResult.takeOkay()}; 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(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(FormatType::String): spec.formatType = FormatType::String; break; case static_cast(FormatType::Escaped): spec.formatType = FormatType::Escaped; break; case static_cast(FormatType::BinaryLower): spec.formatType = FormatType::BinaryLower; break; case static_cast(FormatType::BinaryUpper): spec.formatType = FormatType::BinaryUpper; break; case static_cast(FormatType::Character): spec.formatType = FormatType::Character; break; case static_cast(FormatType::Decimal): spec.formatType = FormatType::Decimal; break; case static_cast(FormatType::Octal): spec.formatType = FormatType::Octal; break; case static_cast(FormatType::HexLower): spec.formatType = FormatType::HexLower; break; case static_cast(FormatType::HexUpper): spec.formatType = FormatType::HexUpper; break; case static_cast(FormatType::FloatHexLower): spec.formatType = FormatType::FloatHexLower; break; case static_cast(FormatType::FloatHexUpper): spec.formatType = FormatType::FloatHexUpper; break; case static_cast(FormatType::ScientificLower): spec.formatType = FormatType::ScientificLower; break; case static_cast(FormatType::ScientificUpper): spec.formatType = FormatType::ScientificUpper; break; case static_cast(FormatType::FixedLower): spec.formatType = FormatType::FixedLower; break; case static_cast(FormatType::FixedUpper): spec.formatType = FormatType::FixedUpper; break; case static_cast(FormatType::GeneralLower): spec.formatType = FormatType::GeneralLower; break; case static_cast(FormatType::GeneralUpper): spec.formatType = FormatType::GeneralUpper; break; case static_cast(FormatType::Unspecified): default: return FudStatus::FormatInvalid; } formatView.advanceUnsafe(); return FudStatus::Success; } size_t findSpec(StringView scanView) { size_t index = 0; bool foundBracket = false; while (index < scanView.length()) { auto letter = scanView[index]; if (letter == FormatSpec::openBracket && not foundBracket) { foundBracket = true; } else if (letter == FormatSpec::openBracket && foundBracket) { foundBracket = false; } else if (foundBracket) { index--; break; } index++; } return index; } FudStatus fillUnsignedBuffer( Array& buffer, uint64_t value, uint8_t& bufferLength, Radix radix, bool uppercase) { static_assert(maxIntCharCount < std::numeric_limits::max()); bufferLength = 0; constexpr Array(Radix::Hexadecimal)> lower{ {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}}; constexpr Array(Radix::Hexadecimal)> upper{ {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}}; const auto& lookup = uppercase ? upper : lower; if (value == 0) { buffer[0] = '0'; bufferLength = 1; return FudStatus::Success; } static_assert(std::is_same_v, uint8_t>); auto radixValue = static_cast(radix); while (value > 0 && bufferLength < maxIntCharCount) { auto digit = static_cast(value % radixValue); buffer[bufferLength] = lookup[digit]; value /= radixValue; bufferLength++; } if (value > 0 || bufferLength > maxIntCharCount) { return FudStatus::Failure; } // TODO: implement fud_algorithm reverse for (size_t idx = 0; idx < bufferLength / 2; ++idx) { auto rhsIndex = bufferLength - idx - 1; std::swap(buffer[idx], buffer[rhsIndex]); } return FudStatus::Success; } [[nodiscard]] Result validateFloatFormatType(FormatType formatType) { using RetType = Result; auto uppercase = false; switch (formatType) { case FormatType::Unspecified: break; case FormatType::FloatHexLower: break; case FormatType::FloatHexUpper: uppercase = true; break; case FormatType::ScientificLower: break; case FormatType::ScientificUpper: uppercase = true; break; case FormatType::FixedLower: break; case FormatType::FixedUpper: uppercase = true; break; case FormatType::GeneralLower: break; case FormatType::GeneralUpper: uppercase = true; break; case FormatType::Octal: case FormatType::HexLower: case FormatType::HexUpper: case FormatType::Decimal: case FormatType::Character: case FormatType::BinaryLower: case FormatType::BinaryUpper: case FormatType::String: case FormatType::Escaped: return RetType::error(FudStatus::FormatInvalid); break; default: return RetType::error(FudStatus::Failure); } return RetType::okay(uppercase); } ExponentBuffer getScientificExponent(int exponent, uint8_t& exponentLength, bool uppercase) { IntCharArray buffer{}; uint8_t bufferLength = 0; auto exponentStatus = fillSignedBuffer(buffer, exponent, bufferLength, Radix::Decimal, true); fudAssert(exponentStatus == FudStatus::Success); fudAssert(bufferLength <= maxDecimalExpLength); ExponentBuffer exponentBuffer{}; exponentBuffer[0] = uppercase ? 'E' : 'e'; exponentBuffer[1] = exponent < 0 ? '-' : '+'; switch (bufferLength) { case 0: fudAssert(bufferLength > 0); break; case 1: exponentBuffer[2] = '0'; exponentBuffer[3] = buffer[0]; break; case 2: exponentBuffer[2] = buffer[0]; exponentBuffer[3] = buffer[1]; break; case 3: exponentBuffer[2] = buffer[0]; exponentBuffer[3] = buffer[1]; exponentBuffer[4] = buffer[2]; break; default: fudAssert(bufferLength <= maxDecimalExpLength); } exponentLength = bufferLength < 3 ? 4 : 5; return exponentBuffer; } } // namespace impl } // namespace fud