diff options
Diffstat (limited to 'include')
-rw-r--r-- | include/fud_assert.hpp | 4 | ||||
-rw-r--r-- | include/fud_format.hpp | 448 |
2 files changed, 294 insertions, 158 deletions
diff --git a/include/fud_assert.hpp b/include/fud_assert.hpp index 6b21fdc..1b5fe9f 100644 --- a/include/fud_assert.hpp +++ b/include/fud_assert.hpp @@ -31,6 +31,10 @@ namespace fud { #define fudAssert(expr) ((expr) ? static_cast<void>(0) : assertFail(#expr, std::source_location::current())) +namespace impl { +void assertFailMessage(const char* assertion, std::source_location sourceLocation); +} + } // namespace fud #endif diff --git a/include/fud_format.hpp b/include/fud_format.hpp index c42cd03..1f2e0ae 100644 --- a/include/fud_format.hpp +++ b/include/fud_format.hpp @@ -224,6 +224,7 @@ struct FormatHandle { using FormatArgument = std::variant< bool, + char, utf8, int32_t, uint32_t, @@ -233,6 +234,7 @@ using FormatArgument = std::variant< double, long double, const utf8*, + const char*, StringView, const void*, impl::FormatHandle>; @@ -288,6 +290,9 @@ template <typename Sink> FormatResult format(Sink& sink, FormatCharMode formatMode, const FormatSpec& formatSpec, bool arg); template <typename Sink> +FormatResult format(Sink& sink, FormatCharMode formatMode, const FormatSpec& formatSpec, char arg); + +template <typename Sink> FormatResult format(Sink& sink, FormatCharMode formatMode, const FormatSpec& formatSpec, utf8 arg); template <typename Sink> @@ -315,6 +320,9 @@ template <typename Sink> FormatResult format(Sink& sink, FormatCharMode formatMode, const FormatSpec& formatSpec, const utf8* arg); template <typename Sink> +FormatResult format(Sink& sink, FormatCharMode formatMode, const FormatSpec& formatSpec, const char* arg); + +template <typename Sink> FormatResult format(Sink& sink, FormatCharMode formatMode, const FormatSpec& formatSpec, StringView arg); template <typename Sink> @@ -328,6 +336,8 @@ namespace impl { template <typename Sink, size_t Size> FormatResult vFormat(Sink& sink, FormatCharMode formatMode, FormatString fmt, const FormatArguments<Size>& args); +size_t findSpec(StringView scanView); + } // namespace impl template <typename Sink, typename... Args> @@ -347,19 +357,22 @@ FormatResult format(Sink& sink, FormatCharMode formatMode, FormatString fmt, Arg template <typename Sink> FormatResult format(Sink& sink, FormatCharMode formatMode, FormatString fmt) { - static_cast<void>(sink); - static_cast<void>(fmt); if (formatMode != FormatCharMode::Unchecked) { return {0, FudStatus::NotImplemented}; } - return {0, FudStatus::NotImplemented}; + auto scanView = fmt.view(); + auto specIndex = impl::findSpec(scanView); + if (specIndex < scanView.length()) { + return {0, FudStatus::FormatInvalid}; + } + + auto drainResult = sink.drain(fmt.view()); + return FormatResult{drainResult.bytesWritten, drainResult.status}; } namespace impl { -size_t findSpec(StringView scanView); - template <size_t Size> Result<uint32_t, FudStatus> getSpecField(FormatSpec& formatSpec, uint32_t index, const FormatArguments<Size>& args); @@ -448,26 +461,6 @@ FormatResult vFormat(Sink& sink, FormatCharMode formatMode, FormatString fmt, co return result; } -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; -} - template <size_t Size> Result<uint32_t, FudStatus> getSpecField(FormatSpec& formatSpec, uint32_t index, const FormatArguments<Size>& args) { @@ -653,7 +646,7 @@ FudStatus vFormatPrepareNonPositionalSpec( template <typename T> requires std::is_integral_v<T> -[[nodiscard]] StringView validateFormatType( +[[nodiscard]] StringView validateIntegralFormatType( FormatResult& result, FormatType formatType, Radix& radix, @@ -710,95 +703,77 @@ template <typename T> case FormatType::GeneralLower: case FormatType::GeneralUpper: result.status = FudStatus::FormatInvalid; + break; default: result.status = FudStatus::Failure; } return prefix; } -inline FudStatus fillUnsignedBuffer( +FudStatus fillUnsignedBuffer( Array<utf8, maxIntCharCount>& buffer, uint64_t value, uint8_t& bufferLength, Radix radix, + bool uppercase); + +template <typename T> +FudStatus fillSignedBuffer( + Array<utf8, maxIntCharCount>& buffer, + T value, + uint8_t& bufferLength, + Radix radix, bool uppercase) { - static_assert(maxIntCharCount < std::numeric_limits<uint8_t>::max()); - bufferLength = 0; - - constexpr Array<uint8_t, static_cast<size_t>(Radix::Hexadecimal)> lower{ - {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}}; - constexpr Array<uint8_t, static_cast<size_t>(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<std::underlying_type_t<Radix>, uint8_t>); - auto radixValue = static_cast<uint8_t>(radix); - while (value > 0 && bufferLength < maxIntCharCount) { - auto digit = static_cast<uint8_t>(value % radixValue); - buffer[bufferLength] = lookup[digit]; - value /= radixValue; - bufferLength++; - } - - if (value > 0 || bufferLength > maxIntCharCount) { - return FudStatus::Failure; + static_assert(sizeof(T) <= sizeof(uint64_t)); + static_assert(std::is_signed_v<T>); + if (value < 0) { + value++; + value = -value; + value++; } - - // 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]); + if constexpr (std::is_same_v<T, char>) { + return fillUnsignedBuffer(buffer, static_cast<uint8_t>(value), bufferLength, radix, uppercase); + } else if constexpr (std::is_same_v<T, int8_t>) { + return fillUnsignedBuffer(buffer, static_cast<uint8_t>(value), bufferLength, radix, uppercase); + } else if constexpr (std::is_same_v<T, int16_t>) { + return fillUnsignedBuffer(buffer, static_cast<uint16_t>(value), bufferLength, radix, uppercase); + } else if constexpr (std::is_same_v<T, int32_t>) { + return fillUnsignedBuffer(buffer, static_cast<uint32_t>(value), bufferLength, radix, uppercase); + } else if constexpr (std::is_same_v<T, int64_t>) { + return fillUnsignedBuffer(buffer, static_cast<uint64_t>(value), bufferLength, radix, uppercase); } - - return FudStatus::Success; } template <typename Sink> -[[nodiscard]] FormatResult fillPad(Sink& sink, utf8 fill, size_t count) -{ - FormatResult result{0, FudStatus::Success}; - constexpr size_t BigBlockSize = 256; - constexpr size_t MediumBlockSize = 64; - constexpr size_t SmallBlockSize = 16; - constexpr size_t SmallestBlockSize = 4; - - auto backingBuffer = Array<utf8, BigBlockSize>::constFill(fill); - - constexpr auto bufferSizes{Array<size_t, 4>{{BigBlockSize, MediumBlockSize, SmallBlockSize, SmallestBlockSize}}}; - - for (auto bufferSize : bufferSizes) { - while (count > bufferSize) { - auto drainResult = sink.drain(StringView{bufferSize, backingBuffer.data()}); - result.bytesWritten += drainResult.bytesWritten; - result.status = drainResult.status; - if (result.status != FudStatus::Success) { - return result; - } - count -= result.bytesWritten; - } - } +[[nodiscard]] FormatResult fillPad(Sink& sink, utf8 fill, size_t count); - if (count > 0) { - fudAssert(count < backingBuffer.size()); - auto drainResult = sink.drain(StringView{count, backingBuffer.data()}); - result.bytesWritten += drainResult.bytesWritten; - result.status = drainResult.status; - } +template <typename Sink> +FormatResult drainIntegral( + Sink& sink, + StringView buffer, + FormatAlign::Value align, + utf8 fill, + size_t width, + StringView sign, + StringView prefix, + bool padZero); - return result; -} +template <typename T> +FudStatus fillIntegralBuffer( + FormatType formatType, + Array<utf8, maxIntCharCount>& buffer, + uint8_t& bufferLength, + T arg, + Radix radix, + bool uppercase); template <typename Sink, typename T> - requires std::is_integral_v<T> and std::is_scalar_v<T> /* and std::is_unsigned_v<T> */ -[[nodiscard]] FormatResult formatUnsigned(Sink& sink, FormatCharMode formatMode, const FormatSpec& formatSpec, T arg) + requires std::is_integral_v<T> and std::is_scalar_v<T> +[[nodiscard]] FormatResult formatIntegral(Sink& sink, FormatCharMode formatMode, const FormatSpec& formatSpec, T arg) { + static_cast<void>(formatMode); + FormatResult result{0, FudStatus::Success}; if (formatSpec.takesPrecision || formatSpec.precision != FormatSpec::precisionUnspecified) { result.status = FudStatus::FormatInvalid; @@ -820,32 +795,23 @@ template <typename Sink, typename T> auto radix = Radix::Decimal; bool uppercase = false; - auto prefix = validateFormatType(result, formatSpec.formatType, radix, uppercase, arg); + auto prefix = validateIntegralFormatType(result, formatSpec.formatType, radix, uppercase, arg); if (result.status != FudStatus::Success) { return result; } - if (formatSpec.formatType == FormatType::Character) { - return format(sink, formatMode, formatSpec, static_cast<utf8>(arg)); - } - - if (not formatSpec.alternateForm) { - prefix = StringView{""}; - } - - auto calculatedWidth = prefix.length(); - Array<utf8, maxIntCharCount> buffer{}; uint8_t bufferLength = 0; - // TODO: constexpr if for signed values - result.status = fillUnsignedBuffer(buffer, static_cast<uint64_t>(arg), bufferLength, radix, uppercase); + result.status = fillIntegralBuffer(formatSpec.formatType, buffer, bufferLength, arg, radix, uppercase); if (result.status != FudStatus::Success) { return result; } - calculatedWidth += bufferLength; + if (not formatSpec.alternateForm) { + prefix = StringView{""}; + } StringView sign{""}; switch (formatSpec.formatSign) { @@ -865,20 +831,69 @@ template <typename Sink, typename T> } break; } - calculatedWidth += sign.length(); - if (align == FormatAlign::Value::Right and width > calculatedWidth) { - auto leftPadSize = width - calculatedWidth; - auto padResult = fillPad(sink, fill, leftPadSize); - result.bytesWritten += padResult.bytesWritten; - result.status = padResult.status; - } else if (align == FormatAlign::Value::Center and width > calculatedWidth) { - auto leftPadSize = (width - calculatedWidth) / 2; - auto padResult = fillPad(sink, fill, leftPadSize); - result.bytesWritten += padResult.bytesWritten; - result.status = padResult.status; + return drainIntegral(sink, StringView{bufferLength, buffer.data()}, align, fill, width, sign, prefix, padZero); +} + +template <typename T> +FudStatus fillIntegralBuffer( + FormatType formatType, + Array<utf8, maxIntCharCount>& buffer, + uint8_t& bufferLength, + T arg, + Radix radix, + bool uppercase) +{ + if constexpr (std::is_same_v<T, char>) { + if (formatType == FormatType::Unspecified) { + formatType = FormatType::Character; + } } + if (formatType == FormatType::Character) { + // return format(sink, formatMode, formatSpec, static_cast<utf8>(arg)); + if constexpr (std::is_signed_v<T>) { + if (arg < 0) { + return FudStatus::FormatInvalid; + } + } + if (arg > std::numeric_limits<utf8>::max()) { + return FudStatus::FormatInvalid; + } + bufferLength = 1; + buffer[0] = static_cast<utf8>(arg); + return FudStatus::Success; + } + + // TODO: constexpr if for signed values + static_assert(not std::is_signed_v<utf8>); + static_assert(std::is_signed_v<char>); + if constexpr (std::is_signed_v<T>) { + return fillSignedBuffer(buffer, arg, bufferLength, radix, uppercase); + } + + return fillUnsignedBuffer(buffer, static_cast<uint64_t>(arg), bufferLength, radix, uppercase); +} + +template <typename Sink> +FormatResult leftPad(Sink& sink, FormatAlign::Value align, size_t width, utf8 fill, size_t calculatedWidth); + +template <typename Sink> +FormatResult rightPad(Sink& sink, FormatAlign::Value align, size_t width, utf8 fill, size_t calculatedWidth); + +template <typename Sink> +FormatResult drainIntegral( + Sink& sink, + StringView buffer, + FormatAlign::Value align, + utf8 fill, + size_t width, + StringView sign, + StringView prefix, + bool padZero) +{ + const auto calculatedWidth = buffer.length() + prefix.length() + sign.length(); + auto result = leftPad(sink, align, width, fill, calculatedWidth); if (result.status != FudStatus::Success) { return result; } @@ -894,7 +909,7 @@ template <typename Sink, typename T> } if (prefix.length() > 0) { - auto drainResult = sink.drain(sign); + auto drainResult = sink.drain(prefix); result.bytesWritten += drainResult.bytesWritten; result.status = drainResult.status; } @@ -914,7 +929,7 @@ template <typename Sink, typename T> return result; } - auto drainNumberResult = sink.drain(StringView{bufferLength, buffer.data()}); + auto drainNumberResult = sink.drain(buffer); result.bytesWritten += drainNumberResult.bytesWritten; result.status = drainNumberResult.status; @@ -922,20 +937,76 @@ template <typename Sink, typename T> return result; } + auto rightPadResult = rightPad(sink, align, width, fill, calculatedWidth); + result.bytesWritten += rightPadResult.bytesWritten; + result.status = rightPadResult.status; + + return result; +} + +template <typename Sink> +[[nodiscard]] FormatResult fillPad(Sink& sink, utf8 fill, size_t count) +{ + FormatResult result{0, FudStatus::Success}; + constexpr size_t BigBlockSize = 256; + constexpr size_t MediumBlockSize = 64; + constexpr size_t SmallBlockSize = 16; + constexpr size_t SmallestBlockSize = 4; + + auto backingBuffer = Array<utf8, BigBlockSize>::constFill(fill); + + constexpr auto bufferSizes{Array<size_t, 4>{{BigBlockSize, MediumBlockSize, SmallBlockSize, SmallestBlockSize}}}; + + for (auto bufferSize : bufferSizes) { + while (count > bufferSize) { + auto drainResult = sink.drain(StringView{bufferSize, backingBuffer.data()}); + result.bytesWritten += drainResult.bytesWritten; + result.status = drainResult.status; + if (result.status != FudStatus::Success) { + return result; + } + count -= result.bytesWritten; + } + } + + if (count > 0) { + fudAssert(count < backingBuffer.size()); + auto drainResult = sink.drain(StringView{count, backingBuffer.data()}); + result.bytesWritten += drainResult.bytesWritten; + result.status = drainResult.status; + } + + return result; +} + +template <typename Sink> +FormatResult leftPad(Sink& sink, FormatAlign::Value align, size_t width, utf8 fill, size_t calculatedWidth) +{ + if (align == FormatAlign::Value::Right and width > calculatedWidth) { + auto leftPadSize = width - calculatedWidth; + return fillPad(sink, fill, leftPadSize); + } + if (align == FormatAlign::Value::Center and width > calculatedWidth) { + auto leftPadSize = (width - calculatedWidth) / 2; + return fillPad(sink, fill, leftPadSize); + } + return FormatResult{0, FudStatus::Success}; +} + +template <typename Sink> +FormatResult rightPad(Sink& sink, FormatAlign::Value align, size_t width, utf8 fill, size_t calculatedWidth) +{ + if (align == FormatAlign::Value::Left and width > calculatedWidth) { auto rightPadSize = width - calculatedWidth; - auto padResult = fillPad(sink, fill, rightPadSize); - result.bytesWritten += padResult.bytesWritten; - result.status = padResult.status; - } else if (align == FormatAlign::Value::Center and width > calculatedWidth) { + return fillPad(sink, fill, rightPadSize); + } + if (align == FormatAlign::Value::Center and width > calculatedWidth) { auto leftPadSize = (width - calculatedWidth) / 2; - auto rightPadSize = width - leftPadSize; - auto padResult = fillPad(sink, fill, rightPadSize); - result.bytesWritten += padResult.bytesWritten; - result.status = padResult.status; + auto rightPadSize = width - calculatedWidth - leftPadSize; + return fillPad(sink, fill, rightPadSize); } - - return result; + return FormatResult{0, FudStatus::Success}; } } // namespace impl @@ -943,47 +1014,52 @@ template <typename Sink, typename T> template <typename Sink> FormatResult format(Sink& sink, FormatCharMode formatMode, const FormatSpec& formatSpec, bool arg) { - FormatResult result{0, FudStatus::NotImplemented}; - static_cast<void>(sink); - static_cast<void>(formatMode); - static_cast<void>(formatSpec); - static_cast<void>(arg); - return result; + FormatSpec boolSpec = formatSpec; + if (formatSpec.formatType == FormatType::Unspecified || formatSpec.formatType == FormatType::String) { + StringView view{"true"}; + if (not arg) { + view = StringView{"false"}; + } + return format(sink, formatMode, boolSpec, view); + } + boolSpec.formatType = FormatType::Unspecified; + return impl::formatIntegral(sink, formatMode, boolSpec, static_cast<uint8_t>(arg)); } template <typename Sink> FormatResult format(Sink& sink, FormatCharMode formatMode, const FormatSpec& formatSpec, utf8 arg) { - FormatResult result{0, FudStatus::NotImplemented}; - static_cast<void>(sink); - static_cast<void>(formatMode); - static_cast<void>(formatSpec); - static_cast<void>(arg); - return result; + return impl::formatIntegral(sink, formatMode, formatSpec, arg); +} + +template <typename Sink> +FormatResult format(Sink& sink, FormatCharMode formatMode, const FormatSpec& formatSpec, char arg) +{ + return impl::formatIntegral(sink, formatMode, formatSpec, arg); } template <typename Sink> FormatResult format(Sink& sink, FormatCharMode formatMode, const FormatSpec& formatSpec, int32_t arg) { - return impl::formatUnsigned(sink, formatMode, formatSpec, arg); + return impl::formatIntegral(sink, formatMode, formatSpec, arg); } template <typename Sink> FormatResult format(Sink& sink, FormatCharMode formatMode, const FormatSpec& formatSpec, uint32_t arg) { - return impl::formatUnsigned(sink, formatMode, formatSpec, arg); + return impl::formatIntegral(sink, formatMode, formatSpec, arg); } template <typename Sink> FormatResult format(Sink& sink, FormatCharMode formatMode, const FormatSpec& formatSpec, int64_t arg) { - return impl::formatUnsigned(sink, formatMode, formatSpec, arg); + return impl::formatIntegral(sink, formatMode, formatSpec, arg); } template <typename Sink> FormatResult format(Sink& sink, FormatCharMode formatMode, const FormatSpec& formatSpec, uint64_t arg) { - return impl::formatUnsigned(sink, formatMode, formatSpec, arg); + return impl::formatIntegral(sink, formatMode, formatSpec, arg); } template <typename Sink> @@ -1022,22 +1098,79 @@ FormatResult format(Sink& sink, FormatCharMode formatMode, const FormatSpec& for template <typename Sink> FormatResult format(Sink& sink, FormatCharMode formatMode, const FormatSpec& formatSpec, const utf8* arg) { - FormatResult result{0, FudStatus::NotImplemented}; - static_cast<void>(sink); - static_cast<void>(formatMode); - static_cast<void>(formatSpec); - static_cast<void>(arg); - return result; + return format(sink, formatMode, formatSpec, reinterpret_cast<const char*>(arg)); +} + +template <typename Sink> +FormatResult format(Sink& sink, FormatCharMode formatMode, const FormatSpec& formatSpec, const char* arg) +{ + if (arg == nullptr) { + return {0, FudStatus::NullPointer}; + } + auto stringLengthResult = cStringLength(arg); + if (stringLengthResult < 0 || stringLengthResult >= SSIZE_MAX) { + return {0, FudStatus::StringInvalid}; + } + auto stringLength = static_cast<size_t>(stringLengthResult); + return format(sink, formatMode, formatSpec, StringView{stringLength, arg}); } template <typename Sink> FormatResult format(Sink& sink, FormatCharMode formatMode, const FormatSpec& formatSpec, StringView arg) { - FormatResult result{0, FudStatus::NotImplemented}; - static_cast<void>(sink); - static_cast<void>(formatMode); - static_cast<void>(formatSpec); - static_cast<void>(arg); + /* TODO: implement unicode points, check with formatMode */ + + FormatResult result{0, FudStatus::Success}; + if (formatMode != FormatCharMode::Unchecked) { + result.status = FudStatus::NotSupported; + return result; + } + + if (formatSpec.formatType != FormatType::Unspecified && formatSpec.formatType != FormatType::String && + formatSpec.formatType != FormatType::Escaped) { + result.status = FudStatus::FormatInvalid; + return result; + } + + if (formatSpec.leadingZero) { + result.status = FudStatus::FormatInvalid; + return result; + } + + auto widthSpecified = formatSpec.width != FormatSpec::widthUnspecified; + auto width = widthSpecified ? formatSpec.width : 0; + auto precisionSpecified = formatSpec.precision != FormatSpec::precisionUnspecified; + if (precisionSpecified and formatSpec.precision < arg.length()) { + arg.m_length = formatSpec.precision; + } + auto calculatedWidth = arg.length(); + + auto align = formatSpec.fill.align.value; + auto fill = formatSpec.fill.fill; + if (align == FormatAlign::Value::Default) { + align = FormatAlign::Value::Left; + } + + auto padResult = impl::leftPad(sink, align, width, fill, calculatedWidth); + result.bytesWritten += padResult.bytesWritten; + result.status = padResult.status; + + if (result.status != FudStatus::Success) { + return result; + } + + auto drainViewResult = sink.drain(arg); + result.bytesWritten += drainViewResult.bytesWritten; + result.status = drainViewResult.status; + + if (result.status != FudStatus::Success) { + return result; + } + + padResult = impl::rightPad(sink, align, width, fill, calculatedWidth); + result.bytesWritten += padResult.bytesWritten; + result.status = padResult.status; + return result; } @@ -1064,7 +1197,6 @@ FormatResult format(Sink& sink, FormatCharMode formatMode, const FormatSpec& for } namespace fudTest { - inline void test() { StringView prefix{0, ""}; |