summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/fud_assert.hpp4
-rw-r--r--include/fud_format.hpp448
-rw-r--r--source/fud_assert.cpp63
-rw-r--r--source/fud_format.cpp64
-rw-r--r--test/test_assert.cpp14
-rw-r--r--test/test_format.cpp108
6 files changed, 507 insertions, 194 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, ""};
diff --git a/source/fud_assert.cpp b/source/fud_assert.cpp
index 8811464..0c4d7cf 100644
--- a/source/fud_assert.cpp
+++ b/source/fud_assert.cpp
@@ -1,39 +1,56 @@
#include "fud_assert.hpp"
-// #include "fud_array.hpp"
-// #include "fud_format.hpp"
+#include "fud_format.hpp"
+#include "fud_string.hpp"
#include <climits>
#include <cstdio>
#include <exception>
-// #include <format>
namespace fud {
-// constexpr std::size_t BITS_PER_OCTAL = 3;
-// constexpr auto MAX_LINE_CHARS = BITS_PER_OCTAL * sizeof(decltype(std::source_location{}.line())) + 3;
+struct BufferSink {
+ DrainResult drain(StringView source);
+};
-[[noreturn]] void assertFail(const char* assertion, const std::source_location sourceLocation)
+DrainResult BufferSink::drain(StringView source)
+{
+ DrainResult result{0, FudStatus::Success};
+ if (source.m_data == nullptr) {
+ result.status = FudStatus::NullPointer;
+ return result;
+ }
+ if (source.m_length == 0) {
+ result.status = FudStatus::Success;
+ return result;
+ }
+ result.bytesWritten = fwrite(reinterpret_cast<const char*>(source.m_data), 1, source.m_length, stderr);
+ if (result.bytesWritten != source.m_length) {
+ result.status = FudStatus::Full;
+ }
+ return result;
+}
+
+namespace impl {
+void assertFailMessage(const char* assertion, const std::source_location sourceLocation)
{
- const char* file_name = sourceLocation.file_name();
- if (file_name == nullptr) {
- fputs("Unknown file", stderr);
- } else {
- fputs(file_name, stderr);
+ BufferSink sink;
+ const char* fileName = sourceLocation.file_name();
+ if (fileName == nullptr) {
+ fileName = "Unknown file";
+ }
+ const char* functionName = sourceLocation.function_name();
+ if (functionName == nullptr) {
+ functionName = "Unknown Function";
}
+ format(sink, FormatCharMode::Unchecked, "{}:{}:{}: {}\n", fileName, functionName, sourceLocation.line(), assertion);
+}
- /*
- constexpr std::size_t assertMsgSize = MAX_LINE_CHARS + 3;
- Array<char, assertMsgSize> buffer{};
- static_cast<void>(std::format_to_n(buffer.data(), buffer.size() - 1U, ":{}:", sourceLocation.line()));
- buffer[buffer.size() - 1] = '\0';
- fputs(buffer.data(), stderr);
- */
-
- fputs(sourceLocation.function_name(), stderr);
- fputs(": ", stderr);
- fputs(assertion, stderr);
- fputc('\n', stderr);
+} // namespace impl
+
+[[noreturn]] void assertFail(const char* assertion, const std::source_location sourceLocation)
+{
+ impl::assertFailMessage(assertion, sourceLocation);
std::terminate();
}
diff --git a/source/fud_format.cpp b/source/fud_format.cpp
index 6bd5aae..f3e6894 100644
--- a/source/fud_format.cpp
+++ b/source/fud_format.cpp
@@ -507,6 +507,70 @@ FudStatus getFormatSign(StringView& formatView, FormatSpec& spec)
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<utf8, maxIntCharCount>& buffer,
+ uint64_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;
+ }
+
+ // 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;
+}
+
} // namespace impl
} // namespace fud
diff --git a/test/test_assert.cpp b/test/test_assert.cpp
index 1f95388..5129b97 100644
--- a/test/test_assert.cpp
+++ b/test/test_assert.cpp
@@ -18,6 +18,7 @@
#include "fud_assert.hpp"
#include "gtest/gtest.h"
+#include "gmock/gmock.h"
namespace fud {
@@ -26,4 +27,17 @@ TEST(AssertTest, AssertFud)
EXPECT_EXIT(fudAssert(false), ::testing::KilledBySignal(SIGABRT), ".*");
}
+TEST(AssertTest, AssertFudMessage)
+{
+ testing::internal::CaptureStderr();
+ auto sourceLoc = std::source_location::current();
+ constexpr const char* assertMessage = "Artificial Message";
+ impl::assertFailMessage(assertMessage, sourceLoc);
+ auto message = testing::internal::GetCapturedStderr();
+ EXPECT_THAT(message, testing::HasSubstr(sourceLoc.file_name()));
+ EXPECT_THAT(message, testing::HasSubstr(sourceLoc.function_name()));
+ EXPECT_THAT(message, testing::HasSubstr(std::to_string(sourceLoc.line())));
+ EXPECT_THAT(message, testing::HasSubstr(assertMessage));
+}
+
} // namespace fud
diff --git a/test/test_format.cpp b/test/test_format.cpp
index 9c95330..4a93d98 100644
--- a/test/test_format.cpp
+++ b/test/test_format.cpp
@@ -411,30 +411,112 @@ TEST(FormatTest, FormatTest)
EXPECT_EQ(formatSpecResult.takeError(), FudStatus::FormatInvalid);
}
-TEST(FormatTest, OneArgFormatTest)
+TEST(FormatTest, NoArgFormatTest)
{
String sink{};
- auto formatResult = format(sink, FormatCharMode::Unchecked, "Test {:}", 42U);
+ auto expected = std::format("Test");
+ auto formatResult = format(sink, FormatCharMode::Unchecked, "Test");
EXPECT_TRUE(formatResult.isOkay());
+ EXPECT_STREQ(sink.c_str(), expected.c_str());
ASSERT_EQ(sink.clear(), FudStatus::Success);
- ASSERT_EQ(sink.drain(StringView{4, "TEST"}).bytesWritten, 4);
- ASSERT_TRUE(sink.valid());
- ASSERT_EQ(sink.clear(), FudStatus::Success);
+ expected = std::format("");
+ formatResult = format(sink, FormatCharMode::Unchecked, "");
+ EXPECT_TRUE(formatResult.isOkay());
+ EXPECT_STREQ(sink.c_str(), expected.c_str());
+}
+TEST(FormatTest, OneNumberFormatTest)
+{
+ String sink{};
+ auto formatResult = format(sink, FormatCharMode::Unchecked, "Test {:}", 42U);
+ EXPECT_TRUE(formatResult.isOkay());
+
+ ASSERT_EQ(sink.clear(), FudStatus::Success);
auto expected = std::format("{:6}", 42U);
formatResult = format(sink, FormatCharMode::Unchecked, "{:6}", 42U);
EXPECT_TRUE(formatResult.isOkay());
EXPECT_STREQ(sink.c_str(), expected.c_str());
- /*
- EXPECT_EQ(std::format("{:6}", 'x') , "x ");
- EXPECT_EQ(std::format("{:*<6}", 'x') , "x*****");
- EXPECT_EQ(std::format("{:*>6}", 'x') , "*****x");
- EXPECT_EQ(std::format("{:*^6}", 'x') , "**x***");
- EXPECT_EQ(std::format("{:6d}", c) , " 120");
- EXPECT_EQ(std::format("{:6}", true) , "true ");
- */
+ ASSERT_EQ(sink.clear(), FudStatus::Success);
+ expected = std::format("{:*<6}", std::numeric_limits<uint64_t>::max());
+ formatResult = format(sink, FormatCharMode::Unchecked, "{:*<6}", std::numeric_limits<uint64_t>::max());
+ EXPECT_TRUE(formatResult.isOkay());
+ EXPECT_STREQ(sink.c_str(), expected.c_str());
+
+ ASSERT_EQ(sink.clear(), FudStatus::Success);
+ expected = std::format("{:*<6}", std::numeric_limits<int64_t>::min());
+ formatResult = format(sink, FormatCharMode::Unchecked, "{:*<6}", std::numeric_limits<int64_t>::min());
+ EXPECT_TRUE(formatResult.isOkay());
+ EXPECT_STREQ(sink.c_str(), expected.c_str());
+
+ ASSERT_EQ(sink.clear(), FudStatus::Success);
+ expected = std::format("{:}", std::numeric_limits<int8_t>::min());
+ formatResult = format(sink, FormatCharMode::Unchecked, "{:}", std::numeric_limits<int8_t>::min());
+ EXPECT_TRUE(formatResult.isOkay());
+ EXPECT_STREQ(sink.c_str(), expected.c_str());
+
+ ASSERT_EQ(sink.clear(), FudStatus::Success);
+ expected = std::format("{:#X}", std::numeric_limits<int8_t>::min());
+ formatResult = format(sink, FormatCharMode::Unchecked, "{:#X}", std::numeric_limits<int8_t>::min());
+ EXPECT_TRUE(formatResult.isOkay());
+ EXPECT_STREQ(sink.c_str(), expected.c_str());
+
+ ASSERT_EQ(sink.clear(), FudStatus::Success);
+ expected = std::format("{:*<6}", 42U);
+ formatResult = format(sink, FormatCharMode::Unchecked, "{:*<6}", 42U);
+ EXPECT_TRUE(formatResult.isOkay());
+ EXPECT_STREQ(sink.c_str(), expected.c_str());
+
+ ASSERT_EQ(sink.clear(), FudStatus::Success);
+ expected = std::format("{:*>6}", 42U);
+ formatResult = format(sink, FormatCharMode::Unchecked, "{:*>6}", 42U);
+ EXPECT_TRUE(formatResult.isOkay());
+ EXPECT_STREQ(sink.c_str(), expected.c_str());
+
+ ASSERT_EQ(sink.clear(), FudStatus::Success);
+ expected = std::format("{:*^6}", 42U);
+ formatResult = format(sink, FormatCharMode::Unchecked, "{:*^6}", 42U);
+ EXPECT_TRUE(formatResult.isOkay());
+ EXPECT_STREQ(sink.c_str(), expected.c_str());
+
+ ASSERT_EQ(sink.clear(), FudStatus::Success);
+ expected = std::format("{:*<6}", 'x');
+ formatResult = format(sink, FormatCharMode::Unchecked, "{:*<6}", 'x');
+ EXPECT_TRUE(formatResult.isOkay());
+ EXPECT_STREQ(sink.c_str(), expected.c_str());
+
+ ASSERT_EQ(sink.clear(), FudStatus::Success);
+ expected = std::format("{:6d}", 'x');
+ formatResult = format(sink, FormatCharMode::Unchecked, "{:6d}", 'x');
+ EXPECT_TRUE(formatResult.isOkay());
+ EXPECT_STREQ(sink.c_str(), expected.c_str());
+}
+
+TEST(FormatTest, OneBoolFormatTest)
+{
+ String sink{};
+
+ ASSERT_EQ(sink.clear(), FudStatus::Success);
+ auto expected = std::format("{:6}", true);
+ auto formatResult = format(sink, FormatCharMode::Unchecked, "{:6}", true);
+ EXPECT_TRUE(formatResult.isOkay());
+ EXPECT_STREQ(sink.c_str(), expected.c_str());
+}
+
+TEST(FormatTest, TwoArgFormatTest)
+{
+ String sink{};
+ auto expected = std::format("Test {:} {:}", 1U, false);
+ auto formatResult = format(sink, FormatCharMode::Unchecked, "Test {:} {:}", 1U, false);
+ EXPECT_TRUE(formatResult.isOkay());
+ EXPECT_STREQ(sink.c_str(), expected.c_str());
+
+ ASSERT_EQ(sink.clear(), FudStatus::Success);
+ expected = std::format("Test {:} {:}", 1U, "Hello");
+ formatResult = format(sink, FormatCharMode::Unchecked, "Test {:} {:}", 1U, "Hello");
+ EXPECT_TRUE(formatResult.isOkay());
+ EXPECT_STREQ(sink.c_str(), expected.c_str());
}
} // namespace fud