summaryrefslogtreecommitdiff
path: root/include/fud_format.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'include/fud_format.hpp')
-rw-r--r--include/fud_format.hpp873
1 files changed, 667 insertions, 206 deletions
diff --git a/include/fud_format.hpp b/include/fud_format.hpp
index 1d09422..c42cd03 100644
--- a/include/fud_format.hpp
+++ b/include/fud_format.hpp
@@ -19,6 +19,7 @@
#define FUD_FORMAT_HPP
// #include "fud_assert.hpp"
+#include "fud_array.hpp"
#include "fud_option.hpp"
#include "fud_result.hpp"
#include "fud_status.hpp"
@@ -27,12 +28,17 @@
#include "fud_string_view.hpp"
#include "fud_utf8.hpp"
+#include <concepts>
#include <cstdint>
#include <limits>
#include <variant>
namespace fud {
+// TODO: constants file?
+constexpr size_t bitsPerOctal = 3;
+constexpr size_t maxIntCharCount = bitsPerOctal * sizeof(uint64_t) + 4;
+
struct FormatString {
template <size_t N>
consteval FormatString(const char (&input)[N]) : m_size{N - 1}, m_data{input}
@@ -199,8 +205,9 @@ struct FormatSpec {
bool hasLocale : 1 = false;
- [[nodiscard]] constexpr bool takesPosition() const {
- return position == positionUnspecified;
+ [[nodiscard]] constexpr bool takesPosition() const
+ {
+ return position != positionUnspecified;
}
static Result<FormatSpec, FudStatus> parse(StringView formatView, size_t& specLength);
@@ -216,7 +223,6 @@ struct FormatHandle {
} // namespace impl
using FormatArgument = std::variant<
- std::monostate,
bool,
utf8,
int32_t,
@@ -240,6 +246,11 @@ struct FormatArguments {
return Size;
}
+ constexpr const FormatArgument& operator[](size_t index) const
+ {
+ return arguments[index];
+ }
+
template <typename... Args>
static auto makeFormatArguments(Args&&... args) -> FormatArguments
{
@@ -266,7 +277,7 @@ struct FormatResult {
};
/* TODO : require concept of a sink that takes pushBack() -> FudStatus */
-/* TODO : sink concept also requires append() -> FudStatus */
+/* TODO : sink concept also requires drain() -> FudStatus */
template <typename Sink>
FormatResult format(Sink& sink, FormatCharMode formatMode, FormatString fmt);
@@ -274,58 +285,48 @@ template <typename Sink, typename... Args>
FormatResult format(Sink& sink, FormatCharMode formatMode, FormatString fmt, Args&&... args);
template <typename Sink>
-FormatResult format(Sink& sink, FormatCharMode formatMode, FormatSpec& formatSpec, bool arg);
+FormatResult format(Sink& sink, FormatCharMode formatMode, const FormatSpec& formatSpec, bool arg);
template <typename Sink>
-FormatResult format(Sink& sink, FormatCharMode formatMode, FormatSpec& formatSpec, utf8 arg);
+FormatResult format(Sink& sink, FormatCharMode formatMode, const FormatSpec& formatSpec, utf8 arg);
template <typename Sink>
-FormatResult format(Sink& sink, FormatCharMode formatMode, FormatSpec& formatSpec, int32_t arg);
+FormatResult format(Sink& sink, FormatCharMode formatMode, const FormatSpec& formatSpec, int32_t arg);
template <typename Sink>
-FormatResult format(Sink& sink, FormatCharMode formatMode, FormatSpec& formatSpec, uint32_t arg);
+FormatResult format(Sink& sink, FormatCharMode formatMode, const FormatSpec& formatSpec, uint32_t arg);
template <typename Sink>
-FormatResult format(Sink& sink, FormatCharMode formatMode, FormatSpec& formatSpec, int64_t arg);
+FormatResult format(Sink& sink, FormatCharMode formatMode, const FormatSpec& formatSpec, int64_t arg);
template <typename Sink>
-FormatResult format(Sink& sink, FormatCharMode formatMode, FormatSpec& formatSpec, uint64_t arg);
+FormatResult format(Sink& sink, FormatCharMode formatMode, const FormatSpec& formatSpec, uint64_t arg);
template <typename Sink>
-FormatResult format(Sink& sink, FormatCharMode formatMode, FormatSpec& formatSpec, float arg);
+FormatResult format(Sink& sink, FormatCharMode formatMode, const FormatSpec& formatSpec, float arg);
template <typename Sink>
-FormatResult format(Sink& sink, FormatCharMode formatMode, FormatSpec& formatSpec, double arg);
+FormatResult format(Sink& sink, FormatCharMode formatMode, const FormatSpec& formatSpec, double arg);
template <typename Sink>
-FormatResult format(Sink& sink, FormatCharMode formatMode, FormatSpec& formatSpec, long double arg);
+FormatResult format(Sink& sink, FormatCharMode formatMode, const FormatSpec& formatSpec, long double arg);
template <typename Sink>
-FormatResult format(Sink& sink, FormatCharMode formatMode, FormatSpec& formatSpec, const utf8* arg);
+FormatResult format(Sink& sink, FormatCharMode formatMode, const FormatSpec& formatSpec, const utf8* arg);
template <typename Sink>
-FormatResult format(Sink& sink, FormatCharMode formatMode, FormatSpec& formatSpec, StringView arg);
+FormatResult format(Sink& sink, FormatCharMode formatMode, const FormatSpec& formatSpec, StringView arg);
template <typename Sink>
-FormatResult format(Sink& sink, FormatCharMode formatMode, FormatSpec& formatSpec, const void* arg);
+FormatResult format(Sink& sink, FormatCharMode formatMode, const FormatSpec& formatSpec, const void* arg);
template <typename Sink>
-FormatResult format(Sink& sink, FormatCharMode formatMode, FormatSpec& formatSpec, impl::FormatHandle arg);
+FormatResult format(Sink& sink, FormatCharMode formatMode, const FormatSpec& formatSpec, impl::FormatHandle arg);
namespace impl {
-Result<size_t, FudStatus> validateSpecHelper(FormatString fmt);
-
-struct FormatInfo {
- uint16_t numArgs{0};
- uint16_t maxArg{0};
- bool positional{false};
-};
-
-Result<FormatInfo, FudStatus> validateSpecHelperV2(FormatString fmt);
-
template <typename Sink, size_t Size>
-FormatResult vFormat(Sink& sink, FormatCharMode formatMode, FormatString fmt, FormatArguments<Size>& args);
+FormatResult vFormat(Sink& sink, FormatCharMode formatMode, FormatString fmt, const FormatArguments<Size>& args);
} // namespace impl
@@ -339,16 +340,6 @@ FormatResult format(Sink& sink, FormatCharMode formatMode, FormatString fmt, Arg
constexpr size_t numArgs = sizeof...(args);
auto formatArguments{FormatArguments<numArgs>::makeFormatArguments(std::forward<Args>(args)...)};
- static_cast<void>(formatArguments);
-
- auto numSpecsResult = impl::validateSpecHelper(fmt);
- if (numSpecsResult.isError()) {
- return {0, numSpecsResult.takeError()};
- }
-
- if (numSpecsResult.getOkay() < numArgs) {
- return {0, FudStatus::FormatInvalid};
- }
return impl::vFormat(sink, formatMode, fmt, formatArguments);
}
@@ -367,150 +358,95 @@ FormatResult format(Sink& sink, FormatCharMode formatMode, FormatString fmt)
namespace impl {
-bool nestingHelper(bool& sawBracket, int& nesting, int increment)
-{
- auto inSpec = nesting > 0;
- if (not inSpec && not sawBracket) {
- sawBracket = true;
- return false;
- }
- if (not inSpec && sawBracket) {
- sawBracket = false;
- return false;
- }
-
- nesting += increment;
- return true;
-}
+size_t findSpec(StringView scanView);
-Result<size_t, FudStatus> validateSpecHelper(FormatString fmt)
-{
- auto scanView{fmt.view()};
- const auto length = scanView.length();
- const auto* data = scanView.data();
- int nesting = 0;
- size_t index = 0;
- size_t numGroups = 0;
+template <size_t Size>
+Result<uint32_t, FudStatus> getSpecField(FormatSpec& formatSpec, uint32_t index, const FormatArguments<Size>& args);
- bool sawOpenBracket = false;
- bool sawCloseBracket = false;
- while (index < length) {
- auto letter = data[index];
+template <size_t Size>
+Result<FormatSpec, FudStatus> vFormatPrepareSpec(
+ StringView& scanView,
+ const FormatArguments<Size>& args,
+ const uint32_t& argIndex,
+ bool& firstSpec,
+ bool& takesPosition);
- if (letter == FormatSpec::openBracket) {
- auto incremented = nestingHelper(sawOpenBracket, nesting, 1);
- if (incremented) {
- numGroups++;
- }
- } else if (letter == FormatSpec::closeBracket) {
- nestingHelper(sawCloseBracket, nesting, -1);
- if (sawOpenBracket) {
- sawOpenBracket = false;
- sawCloseBracket = false;
- numGroups++;
- }
- } else {
- if (sawOpenBracket) {
- nesting++;
- numGroups++;
- }
- sawOpenBracket = false;
- sawCloseBracket = false;
- }
- if (nesting < 0 || nesting > 2) {
- return FudStatus::FormatInvalid;
- }
- ++index;
- }
+template <size_t Size>
+Result<FormatSpec, FudStatus> vFormatPrepareFirstSpec(
+ StringView& scanView,
+ const FormatArguments<Size>& args,
+ const uint32_t& argIndex,
+ bool& takesPosition);
- if (nesting != 0 || sawOpenBracket || sawCloseBracket) {
- return FudStatus::FormatInvalid;
- }
+template <size_t Size>
+FudStatus vFormatPreparePositionalSpec(FormatSpec& formatSpec, const FormatArguments<Size>& args);
- return numGroups;
-}
+template <size_t Size>
+FudStatus vFormatPrepareNonPositionalSpec(
+ FormatSpec& formatSpec,
+ const FormatArguments<Size>& args,
+ const uint32_t& argIndex);
-// NOLINTBEGIN(readability-function-cognitive-complexity)
-Result<FormatInfo, FudStatus> validateSpecHelperV2(FormatString fmt)
+template <typename Sink, size_t Size>
+FormatResult vFormat(Sink& sink, FormatCharMode formatMode, FormatString fmt, const FormatArguments<Size>& args)
{
- auto scanView{fmt.view()};
- const auto length = scanView.length();
- const auto* data = scanView.data();
- int nesting = 0;
- size_t index = 0;
- FormatInfo formatInfo{};
-
- bool sawOpenBracket = false;
- bool sawCloseBracket = false;
- bool enteredGroup = false;
-
- bool firstGroup = true;
+ FormatResult result{0, FudStatus::Success};
+ auto scanView = fmt.view();
+ bool firstSpec = true;
+ bool takesPosition = false;
+ uint32_t argIndex = 0;
+ while (scanView.length() > 0) {
+ if (argIndex > Size) {
+ result.status = FudStatus::FormatInvalid;
+ return result;
+ }
- while (index < length) {
- auto inSpec = nesting > 0;
- auto letter = data[index];
+ auto specIndex = findSpec(scanView);
+ fudAssert(specIndex <= scanView.length());
+ StringView run{specIndex, scanView.data()};
+ auto drainResult = sink.drain(run);
+ result.bytesWritten += drainResult.bytesWritten;
+ if (drainResult.status != FudStatus::Success) {
+ result.status = drainResult.status;
+ return result;
+ }
+ scanView.advanceUnsafe(specIndex);
+ if (specIndex == scanView.length()) {
+ return result;
+ }
- if (letter == FormatSpec::openBracket) {
- if (not inSpec) {
- sawOpenBracket = not sawOpenBracket;
- } else {
- nesting++;
- formatInfo.numArgs++;
- enteredGroup = true;
- }
- } else if (letter == FormatSpec::closeBracket) {
- if (not inSpec) {
- sawCloseBracket = not sawCloseBracket;
- } else {
- nesting--;
- }
- if (sawOpenBracket) {
- sawOpenBracket = false;
- sawCloseBracket = false;
- enteredGroup = false;
- formatInfo.numArgs++;
- }
- } else if (enteredGroup) {
- nesting++;
- formatInfo.numArgs++;
- enteredGroup = false;
- sawOpenBracket = false;
- sawCloseBracket = false;
-
- if (classify::isDigit(letter)) {
- if (length <= index) {
- return FudStatus::FormatInvalid;
- }
- StringView specView{length - index, data + index};
- auto positionResult = fromString<decltype(formatInfo.maxArg)>(specView, Radix::Decimal);
- if (positionResult.isError())
- {
- return positionResult.takeError();
- }
- auto positionValue = positionResult.takeOkay();
- if (not firstGroup && not formatInfo.positional) {
- return FudStatus::FormatInvalid;
- }
- formatInfo.maxArg = std::max(formatInfo.maxArg, positionValue.value);
- formatInfo.numArgs++;
- firstGroup = false;
+ auto specResult{vFormatPrepareSpec(scanView, args, argIndex, firstSpec, takesPosition)};
+ if (specResult.isError()) {
+ result.status = specResult.takeError();
+ return result;
+ }
+ auto formatSpec{specResult.takeOkay()};
+ uint32_t formatArgIndex = argIndex;
+ if (formatSpec.takesPosition()) {
+ if (formatSpec.position >= Size) {
+ result.status = specResult.takeError();
+ return result;
}
+ formatArgIndex = formatSpec.position;
}
-
- if (nesting < 0 || nesting > 2) {
- return FudStatus::FormatInvalid;
+ auto argResult{std::visit(
+ [&](const auto& arg) -> FormatResult { return format(sink, formatMode, formatSpec, arg); },
+ args[formatArgIndex])};
+ if (argResult.status != FudStatus::Success) {
+ result.status = argResult.status;
+ return result;
+ }
+ result.bytesWritten += argResult.bytesWritten;
+ if (!formatSpec.takesPosition()) {
+ argIndex++;
+ argIndex += static_cast<uint8_t>(formatSpec.takesWidth);
+ argIndex += static_cast<uint8_t>(formatSpec.takesPrecision);
}
- ++index;
- }
-
- if (nesting != 0 || sawOpenBracket || sawCloseBracket) {
- return FudStatus::FormatInvalid;
}
- return formatInfo;
+ return result;
}
-// NOLINTEND(readability-function-cognitive-complexity)
size_t findSpec(StringView scanView)
{
@@ -533,80 +469,605 @@ size_t findSpec(StringView scanView)
}
template <size_t Size>
-Result<FormatSpec, FudStatus> vFormatPrepareArgs(
+Result<uint32_t, FudStatus> getSpecField(FormatSpec& formatSpec, uint32_t index, const FormatArguments<Size>& args)
+{
+ static_assert(not std::is_signed_v<utf8>);
+
+ if (not formatSpec.takesPosition() || not formatSpec.takesWidth) {
+ return FudStatus::ArgumentInvalid;
+ }
+
+ if (index >= Size) {
+ return FudStatus::FormatInvalid;
+ }
+
+ if (std::holds_alternative<utf8>(args[index])) {
+ return std::get<utf8>(args[index]);
+ }
+ if (std::holds_alternative<int32_t>(args[index])) {
+ auto value = std::get<int32_t>(args[index]);
+ if (value < 0) {
+ return FudStatus::FormatInvalid;
+ }
+ return static_cast<uint32_t>(value);
+ }
+ if (std::holds_alternative<int64_t>(args[index])) {
+ auto value = std::get<int64_t>(args[index]);
+ if (value < 0 || value > std::numeric_limits<uint32_t>::max()) {
+ return FudStatus::FormatInvalid;
+ }
+ return static_cast<uint32_t>(value);
+ }
+ if (std::holds_alternative<uint32_t>(args[index])) {
+ return std::get<uint32_t>(args[index]);
+ }
+ if (std::holds_alternative<uint32_t>(args[index])) {
+ auto value = std::get<uint64_t>(args[index]);
+ if (value > std::numeric_limits<uint32_t>::max()) {
+ return FudStatus::FormatInvalid;
+ }
+ return static_cast<uint32_t>(value);
+ }
+
+ return FudStatus::FormatInvalid;
+}
+
+template <size_t Size>
+Result<FormatSpec, FudStatus> vFormatPrepareSpec(
StringView& scanView,
- FormatArguments<Size>& args,
- size_t& argIndex,
+ const FormatArguments<Size>& args,
+ const uint32_t& argIndex,
bool& firstSpec,
bool& takesPosition)
{
+ if (firstSpec) {
+ firstSpec = false;
+ return vFormatPrepareFirstSpec(scanView, args, argIndex, takesPosition);
+ }
+
size_t specLength{0};
auto formatSpecResult{FormatSpec::parse(scanView, specLength)};
if (formatSpecResult.isError()) {
return formatSpecResult;
}
fudAssert(specLength <= scanView.length());
- auto formatSpec{formatSpecResult.takeOkay()};
- bool specTakesPosition = formatSpec.position != FormatSpec::positionUnspecified;
- if (firstSpec) {
- takesPosition = specTakesPosition;
- firstSpec = false;
- } else if (takesPosition != specTakesPosition) {
- return FudStatus::FormatInvalid;
- } else if (takesPosition && formatSpec.position >= Size) {
+ auto formatSpec{formatSpecResult.getOkay()};
+
+ if (takesPosition != formatSpec.takesPosition() || (takesPosition && formatSpec.position >= Size)) {
return FudStatus::FormatInvalid;
}
- if (formatSpec.takesWidth) {
- if (takesPosition) {
- auto widthPosition = formatSpec.width;
- if (widthPosition >= args.size()) {
- return FudStatus::FormatInvalid;
- }
+
+ if (takesPosition) {
+ auto status = vFormatPreparePositionalSpec(formatSpec, args);
+ if (status != FudStatus::Success) {
+ return status;
+ }
+ } else {
+ auto status = vFormatPrepareNonPositionalSpec(formatSpec, args, argIndex);
+ if (status != FudStatus::Success) {
+ return status;
}
}
- // auto formatResult{format(sink,
- scanView.advanceUnsafe(specLength);
+
+ scanView.advanceUnsafe(specLength);
return formatSpec;
}
-template <typename Sink, size_t Size>
-FormatResult vFormat(Sink& sink, FormatCharMode formatMode, FormatString fmt, FormatArguments<Size>& args)
+template <size_t Size>
+Result<FormatSpec, FudStatus> vFormatPrepareFirstSpec(
+ StringView& scanView,
+ const FormatArguments<Size>& args,
+ const uint32_t& argIndex,
+ bool& takesPosition)
{
- FormatResult result{0, FudStatus::Failure};
- auto scanView = fmt.view();
- bool firstSpec = false;
- bool takesPosition = false;
- size_t argIndex = 0;
- while (scanView.length() > 0) {
- auto specIndex = findSpec(scanView);
- fudAssert(specIndex <= scanView.length());
- StringView run{specIndex, scanView.data()};
- auto status = sink.append(run);
+ size_t specLength{0};
+ auto formatSpecResult{FormatSpec::parse(scanView, specLength)};
+ if (formatSpecResult.isError()) {
+ return formatSpecResult;
+ }
+ fudAssert(specLength <= scanView.length());
+ auto formatSpec{formatSpecResult.takeOkay()};
+ takesPosition = formatSpec.takesPosition();
+
+ if (takesPosition) {
+ auto status = vFormatPreparePositionalSpec(formatSpec, args);
if (status != FudStatus::Success) {
- result.status = status;
- return result;
+ return status;
}
- result.bytesWritten += specIndex;
- scanView.advanceUnsafe(specIndex);
- if (specIndex == scanView.length()) {
- return result;
+ } else {
+ auto status = vFormatPrepareNonPositionalSpec(formatSpec, args, argIndex);
+ if (status != FudStatus::Success) {
+ return status;
}
+ }
+
+ scanView.advanceUnsafe(specLength);
+
+ return formatSpec;
+}
- auto specResult{vFormatPrepareArgs(scanView, args, argIndex, firstSpec, takesPosition)};
+template <size_t Size>
+FudStatus vFormatPreparePositionalSpec(FormatSpec& formatSpec, const FormatArguments<Size>& args)
+{
+ if (not formatSpec.takesPosition()) {
+ return FudStatus::OperationInvalid;
+ }
+
+ if (formatSpec.takesWidth) {
+ auto widthResult = getSpecField(formatSpec, formatSpec.width, args);
+ if (widthResult.isError()) {
+ return widthResult.takeError();
+ }
+ formatSpec.width = widthResult.takeOkay();
+ }
+
+ if (formatSpec.takesPrecision) {
+ auto precisionResult = getSpecField(formatSpec, formatSpec.precision, args);
+ if (precisionResult.isError()) {
+ return precisionResult.takeError();
+ }
+ formatSpec.precision = precisionResult.takeOkay();
+ }
+
+ return FudStatus::Success;
+}
+
+template <size_t Size>
+FudStatus vFormatPrepareNonPositionalSpec(
+ FormatSpec& formatSpec,
+ const FormatArguments<Size>& args,
+ const uint32_t& argIndex)
+{
+ if (formatSpec.takesPosition()) {
+ return FudStatus::OperationInvalid;
+ }
+
+ uint32_t fieldIndex = argIndex + 1;
+
+ if (formatSpec.takesWidth) {
+ if (fieldIndex < argIndex) {
+ return FudStatus::FormatInvalid;
+ }
+ auto widthResult = getSpecField(formatSpec, fieldIndex, args);
+ if (widthResult.isError()) {
+ return widthResult.takeError();
+ }
+ formatSpec.width = widthResult.takeOkay();
+ fieldIndex++;
+ }
+
+ if (formatSpec.takesPrecision) {
+ if (fieldIndex < argIndex) {
+ return FudStatus::FormatInvalid;
+ }
+ auto precisionResult = getSpecField(formatSpec, fieldIndex, args);
+ if (precisionResult.isError()) {
+ return precisionResult.takeError();
+ }
+ formatSpec.precision = precisionResult.takeOkay();
+ }
+
+ return FudStatus::Success;
+}
+
+template <typename T>
+ requires std::is_integral_v<T>
+[[nodiscard]] StringView validateFormatType(
+ FormatResult& result,
+ FormatType formatType,
+ Radix& radix,
+ bool& uppercase,
+ T arg)
+{
+ StringView prefix{""};
+ switch (formatType) {
+ case FormatType::Unspecified:
+ break;
+ case FormatType::BinaryLower:
+ prefix = StringView{"0b"};
+ radix = Radix::Binary;
+ break;
+ case FormatType::BinaryUpper:
+ prefix = StringView{"0B"};
+ radix = Radix::Binary;
+ uppercase = true;
+ break;
+ case FormatType::Character:
+ if constexpr (std::is_signed_v<T>) {
+ if (arg < 0) {
+ result.status = FudStatus::FormatInvalid;
+ }
+ }
+ if (arg > std::numeric_limits<utf8>::max()) {
+ result.status = FudStatus::FormatInvalid;
+ break;
+ }
+ break;
+ case FormatType::Decimal:
+ break;
+ case FormatType::Octal:
+ prefix = StringView{"o"};
+ radix = Radix::Octal;
+ break;
+ case FormatType::HexLower:
+ prefix = StringView{"0x"};
+ radix = Radix::Hexadecimal;
+ break;
+ case FormatType::HexUpper:
+ prefix = StringView{"0X"};
+ radix = Radix::Hexadecimal;
+ uppercase = true;
+ break;
+ case FormatType::FloatHexLower:
+ case FormatType::FloatHexUpper:
+ case FormatType::String:
+ case FormatType::Escaped:
+ case FormatType::ScientificLower:
+ case FormatType::ScientificUpper:
+ case FormatType::FixedLower:
+ case FormatType::FixedUpper:
+ case FormatType::GeneralLower:
+ case FormatType::GeneralUpper:
+ result.status = FudStatus::FormatInvalid;
+ default:
+ result.status = FudStatus::Failure;
+ }
+ return prefix;
+}
+
+inline 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;
+}
+
+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, 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)
+{
+ FormatResult result{0, FudStatus::Success};
+ if (formatSpec.takesPrecision || formatSpec.precision != FormatSpec::precisionUnspecified) {
+ result.status = FudStatus::FormatInvalid;
+ return result;
+ }
+ auto fill = formatSpec.fill.fill;
+ auto align = formatSpec.fill.align.value;
+ bool padZero = false;
+ if (align == FormatAlign::Value::Default) {
+ align = FormatAlign::Value::Right;
+ fill = formatSpec.leadingZero ? '0' : fill;
+ padZero = formatSpec.leadingZero;
+ }
+ auto width = formatSpec.width;
+ if (width == FormatSpec::widthUnspecified) {
+ width = 0;
+ }
+
+ auto radix = Radix::Decimal;
+ bool uppercase = false;
+
+ auto prefix = validateFormatType(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);
+
+ if (result.status != FudStatus::Success) {
+ return result;
+ }
+
+ calculatedWidth += bufferLength;
+
+ StringView sign{""};
+ switch (formatSpec.formatSign) {
+ case FormatSign::Plus:
+ sign = StringView{"+"};
+ break;
+ case FormatSign::Space:
+ sign = StringView{" "};
+ break;
+ case FormatSign::Minus:
+ case FormatSign::Default:
+ default:
+ if constexpr (std::is_signed_v<T>) {
+ if (arg < 0) {
+ sign = StringView{"-"};
+ }
+ }
+ 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;
+ }
+
+ if (result.status != FudStatus::Success) {
+ return result;
+ }
+
+ if (sign.length() > 0) {
+ auto drainResult = sink.drain(sign);
+ result.bytesWritten += drainResult.bytesWritten;
+ result.status = drainResult.status;
+ }
+
+ if (result.status != FudStatus::Success) {
+ return result;
+ }
+
+ if (prefix.length() > 0) {
+ auto drainResult = sink.drain(sign);
+ result.bytesWritten += drainResult.bytesWritten;
+ result.status = drainResult.status;
+ }
+
+ if (result.status != FudStatus::Success) {
+ return result;
+ }
+
+ if (padZero) {
+ auto leftPadSize = width - calculatedWidth;
+ auto padResult = fillPad(sink, '0', leftPadSize);
+ result.bytesWritten += padResult.bytesWritten;
+ result.status = padResult.status;
+ }
+
+ if (result.status != FudStatus::Success) {
+ return result;
+ }
+
+ auto drainNumberResult = sink.drain(StringView{bufferLength, buffer.data()});
+ result.bytesWritten += drainNumberResult.bytesWritten;
+ result.status = drainNumberResult.status;
+
+ if (result.status != FudStatus::Success) {
+ return result;
+ }
+
+ 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) {
+ auto leftPadSize = (width - calculatedWidth) / 2;
+ auto rightPadSize = width - leftPadSize;
+ auto padResult = fillPad(sink, fill, rightPadSize);
+ result.bytesWritten += padResult.bytesWritten;
+ result.status = padResult.status;
}
- static_cast<void>(formatMode);
- static_cast<void>(args);
return result;
}
} // namespace impl
+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;
+}
+
+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;
+}
+
+template <typename Sink>
+FormatResult format(Sink& sink, FormatCharMode formatMode, const FormatSpec& formatSpec, int32_t arg)
+{
+ return impl::formatUnsigned(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);
+}
+
+template <typename Sink>
+FormatResult format(Sink& sink, FormatCharMode formatMode, const FormatSpec& formatSpec, int64_t arg)
+{
+ return impl::formatUnsigned(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);
+}
+
+template <typename Sink>
+FormatResult format(Sink& sink, FormatCharMode formatMode, const FormatSpec& formatSpec, float arg)
+{
+ FormatResult result{0, FudStatus::NotImplemented};
+ static_cast<void>(sink);
+ static_cast<void>(formatMode);
+ static_cast<void>(formatSpec);
+ static_cast<void>(arg);
+ return result;
+}
+
+template <typename Sink>
+FormatResult format(Sink& sink, FormatCharMode formatMode, const FormatSpec& formatSpec, double arg)
+{
+ FormatResult result{0, FudStatus::NotImplemented};
+ static_cast<void>(sink);
+ static_cast<void>(formatMode);
+ static_cast<void>(formatSpec);
+ static_cast<void>(arg);
+ return result;
+}
+
+template <typename Sink>
+FormatResult format(Sink& sink, FormatCharMode formatMode, const FormatSpec& formatSpec, long double arg)
+{
+ FormatResult result{0, FudStatus::NotImplemented};
+ static_cast<void>(sink);
+ static_cast<void>(formatMode);
+ static_cast<void>(formatSpec);
+ static_cast<void>(arg);
+ return result;
+}
+
+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;
+}
+
+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);
+ return result;
+}
+
+template <typename Sink>
+FormatResult format(Sink& sink, FormatCharMode formatMode, const FormatSpec& formatSpec, const void* arg)
+{
+ FormatResult result{0, FudStatus::NotImplemented};
+ static_cast<void>(sink);
+ static_cast<void>(formatMode);
+ static_cast<void>(formatSpec);
+ static_cast<void>(arg);
+ return result;
+}
+
+template <typename Sink>
+FormatResult format(Sink& sink, FormatCharMode formatMode, const FormatSpec& formatSpec, impl::FormatHandle arg)
+{
+ FormatResult result{0, FudStatus::NotImplemented};
+ static_cast<void>(sink);
+ static_cast<void>(formatMode);
+ static_cast<void>(formatSpec);
+ static_cast<void>(arg);
+ return result;
+}
+
namespace fudTest {
inline void test()
{
+ StringView prefix{0, ""};
String sink{};
static_cast<void>(format(sink, FormatCharMode::Unchecked, "HELL YEAH"));