diff options
| -rw-r--r-- | kapi/include/kapi/memory/address.hpp | 8 | ||||
| -rw-r--r-- | kernel/kstd/print.cpp | 137 | ||||
| -rw-r--r-- | libs/kstd/include/kstd/bits/format_args.hpp | 62 | ||||
| -rw-r--r-- | libs/kstd/include/kstd/bits/format_context.hpp | 89 | ||||
| -rw-r--r-- | libs/kstd/include/kstd/bits/format_specifiers.hpp | 204 | ||||
| -rw-r--r-- | libs/kstd/include/kstd/bits/format_specs.hpp | 104 | ||||
| -rw-r--r-- | libs/kstd/include/kstd/bits/format_string.hpp | 151 | ||||
| -rw-r--r-- | libs/kstd/include/kstd/bits/formatter.hpp | 325 | ||||
| -rw-r--r-- | libs/kstd/include/kstd/bits/print_sink.hpp | 2 | ||||
| -rw-r--r-- | libs/kstd/include/kstd/os/print.hpp | 2 | ||||
| -rw-r--r-- | libs/kstd/include/kstd/print | 20 |
11 files changed, 692 insertions, 412 deletions
diff --git a/kapi/include/kapi/memory/address.hpp b/kapi/include/kapi/memory/address.hpp index 587aeac..13bdf4c 100644 --- a/kapi/include/kapi/memory/address.hpp +++ b/kapi/include/kapi/memory/address.hpp @@ -229,13 +229,13 @@ namespace kstd { constexpr auto static suffix = Type == kapi::memory::address_type::linear ? "%lin" : "%phy"; - constexpr auto parse(std::string_view context) -> std::string_view + constexpr auto parse(format_parse_context & context) -> format_parse_context::iterator { auto result = formatter<std::uintptr_t>::parse(context); - if (!this->specs.type) + if (!this->specifiers.type) { - this->specs.type = 'p'; - this->specs.alternative_form = true; + this->specifiers.type = 'p'; + this->specifiers.alternative_form = true; } return result; } diff --git a/kernel/kstd/print.cpp b/kernel/kstd/print.cpp index c7d26ba..44f80a7 100644 --- a/kernel/kstd/print.cpp +++ b/kernel/kstd/print.cpp @@ -70,74 +70,113 @@ namespace kstd::os auto writer = write_buffer{(sink == print_sink::stderr) ? kapi::cio::output_stream::stderr : kapi::cio::output_stream::stdout}; auto context = kstd::format_context{.writer = write_buffer::callback, .user_data = &writer}; + auto parse_context = kstd::format_parse_context{format, args.size()}; - auto current = format.begin(); - auto end = format.end(); - auto next_automatic_index = 0uz; + auto it = parse_context.begin(); + auto end = parse_context.end(); - while (current != end) + while (it != end) { - if (*current != '{') + if (*it != '{' && *it != '}') { - auto start = current; - while (current != end && *current != '{') + auto start = it; + while (it != end && *it != '{' && *it != '}') { - std::advance(current, 1); + std::advance(it, 1); } - context.push(std::string_view(start, current - start)); + parse_context.advance_to(it); + context.push(std::string_view(start, it - start)); continue; } - if (std::next(current) != end && *(std::next(current)) == '{') + if (*it == '{') { - context.push('{'); - std::advance(current, 2); - continue; - } + std::advance(it, 1); + if (it != end && *it == '{') + { + context.push('{'); + std::advance(it, 1); + parse_context.advance_to(it); + continue; + } - std::advance(current, 1); + parse_context.advance_to(it); + auto index = 0uz; - auto index = 0uz; - if (current != end && *current >= '0' && *current <= '9') - { - while (current != end && *current >= '0' && *current <= '9') + if (it != end && *it >= '0' && *it <= '9') { - index = index * 10 + static_cast<std::size_t>(*current - '0'); - std::advance(current, 1); + while (it != end && *it >= '0' && *it <= '9') + { + index = index * 10 + static_cast<std::size_t>(*it - '0'); + std::advance(it, 1); + } + parse_context.check_arg_id(index); + } + else + { + index = parse_context.next_arg_id(); } - } - else - { - index = next_automatic_index++; - } - auto remaining_fmt = std::string_view{current, static_cast<std::size_t>(std::distance(current, end))}; + if (it != end && *it == ':') + { + std::advance(it, 1); + } - auto const arg = args.get(index); - if (arg.format) - { - auto const after_specs = arg.format(arg.value, remaining_fmt, context); - auto const consumed = remaining_fmt.size() - after_specs.size(); - std::advance(current, consumed); - } - else - { - context.push("{?}"); - while (current != end && *current != '}') - std::advance(current, 1); - } + parse_context.advance_to(it); - if (current != end && *current == '}') - { - std::advance(current, 1); + if (index < args.size()) + { + auto const & arg = args[index]; + if (arg.format_function) + { + arg.format_function(arg.value_pointer, parse_context, context); + } + else + { + context.push("{?}"); + } + } + else + { + context.push("{fmt-err: bound}"); + } + + it = parse_context.begin(); + + if (it != end && *it == '}') + { + std::advance(it, 1); + parse_context.advance_to(it); + } + else + { + context.push("{fmt-err: unconsumed}"); + while (it != end && *it != '}') + { + std::advance(it, 1); + } + + if (it != end) + { + std::advance(it, 1); + parse_context.advance_to(it); + } + } } - else + else if (*it == '}') { - context.push("{fmt-err}"); - while (current != end && *current != '}') - std::advance(current, 1); - if (current != end) - std::advance(current, 1); + std::advance(it, 1); + if (it != end && *it == '}') + { + context.push('}'); + std::advance(it, 1); + parse_context.advance_to(it); + } + else + { + context.push("{fmt-err: unescaped}"); + parse_context.advance_to(it); + } } } } diff --git a/libs/kstd/include/kstd/bits/format_args.hpp b/libs/kstd/include/kstd/bits/format_args.hpp new file mode 100644 index 0000000..d236a23 --- /dev/null +++ b/libs/kstd/include/kstd/bits/format_args.hpp @@ -0,0 +1,62 @@ +#ifndef KSTD_BITS_FORMAT_ARGS_HPP +#define KSTD_BITS_FORMAT_ARGS_HPP + +// IWYU pragma: private, include <kstd/format> + +#include "kstd/bits/format_context.hpp" + +#include <array> +#include <cstddef> +#include <span> +#include <type_traits> + +namespace kstd +{ + template<typename> + struct formatter; + + struct format_arg + { + using format_function_type = auto(void const * value, format_parse_context & parse_context, + format_context & context) -> void; + + void const * value_pointer; + format_function_type * format_function; + }; + + using format_args = std::span<format_arg const>; + + template<std::size_t Count> + struct format_arg_store + { + std::array<format_arg, Count> args{}; + }; + + template<typename ValueType> + auto format_trampoline(void const * value_pointer, format_parse_context & parse_context, format_context & context) + -> void + { + auto typed_value_pointer = static_cast<ValueType const *>(value_pointer); + auto fmt = formatter<std::remove_cvref_t<ValueType>>{}; + auto const it = fmt.parse(parse_context); + parse_context.advance_to(it); + fmt.format(*typed_value_pointer, context); + } + + template<typename... Arguments> + [[nodiscard]] auto make_format_args(Arguments const &... args) noexcept -> format_arg_store<sizeof...(Arguments)> + { + if constexpr (sizeof...(Arguments) == 0) + { + return format_arg_store<0>{}; + } + else + { + return format_arg_store<sizeof...(Arguments)>{std::array<format_arg, sizeof...(Arguments)>{ + format_arg{static_cast<void const *>(&args), format_trampoline<Arguments>}...}}; + } + } + +} // namespace kstd + +#endif
\ No newline at end of file diff --git a/libs/kstd/include/kstd/bits/format_context.hpp b/libs/kstd/include/kstd/bits/format_context.hpp index b5c7d21..863047d 100644 --- a/libs/kstd/include/kstd/bits/format_context.hpp +++ b/libs/kstd/include/kstd/bits/format_context.hpp @@ -3,11 +3,100 @@ // IWYU pragma: private, include <kstd/format> +#include "kstd/os/error.hpp" + +#include <cstddef> #include <string_view> namespace kstd { + constexpr auto report_format_error(char const * message) -> void + { + if consteval + { + extern void compile_time_format_error_triggered(char const *); + compile_time_format_error_triggered(message); + } + else + { + kstd::os::panic("Error while formatting a string."); + } + } + + struct format_parse_context + { + using iterator = std::string_view::const_iterator; + + constexpr format_parse_context(std::string_view format, std::size_t argument_count) + : m_current(format.begin()) + , m_end(format.end()) + , m_argument_count(argument_count) + {} + + [[nodiscard]] constexpr auto begin() const -> iterator + { + return m_current; + } + + [[nodiscard]] constexpr auto end() const -> iterator + { + return m_end; + } + + constexpr auto advance_to(iterator position) -> void + { + m_current = position; + } + + constexpr auto next_arg_id() -> std::size_t + { + if (m_mode == index_mode::manual) + { + report_format_error("Cannot mix automatic and manual indexing."); + } + + m_mode = index_mode::automatic; + + if (m_next_argument_id >= m_argument_count) + { + report_format_error("Argument index out of bounds."); + } + return m_next_argument_id++; + } + + constexpr auto check_arg_id(std::size_t index) -> void + { + if (m_mode == index_mode::automatic) + { + report_format_error("Cannot mix automatic and manual indexing."); + } + + m_mode = index_mode::manual; + + if (index >= m_argument_count) + { + report_format_error("Argument index out of bounds."); + } + } + + private: + enum class index_mode + { + unknown, + automatic, + manual, + }; + + iterator m_current{}; + iterator m_end{}; + index_mode m_mode{}; + std::size_t m_next_argument_id{}; + std::size_t m_argument_count{}; + + public: + }; + struct format_context { using writer_function = void(void *, std::string_view); diff --git a/libs/kstd/include/kstd/bits/format_specifiers.hpp b/libs/kstd/include/kstd/bits/format_specifiers.hpp new file mode 100644 index 0000000..8fb50ef --- /dev/null +++ b/libs/kstd/include/kstd/bits/format_specifiers.hpp @@ -0,0 +1,204 @@ +#ifndef KSTD_BITS_FORMAT_SPECS_HPP +#define KSTD_BITS_FORMAT_SPECS_HPP + +// IWYU pragma: private + +#include "kstd/bits/format_context.hpp" + +#include <cstddef> +#include <cstdint> +#include <iterator> + +namespace kstd::bits +{ + enum struct alignment : std::uint8_t + { + none, + left, + right, + center, + }; + + enum struct sign_mode : std::uint8_t + { + none, + plus, + minus, + space, + }; + + enum struct width_mode : std::uint8_t + { + none, + static_value, + dynamic_argument_id + }; + + struct format_specifiers + { + char fill{' '}; + alignment align{}; + sign_mode sign{}; + bool alternative_form{}; + bool zero_pad{}; + + width_mode width_mode{}; + std::size_t width_value{}; + char type{}; + }; + + struct format_padding + { + std::size_t left{}; + std::size_t right{}; + }; + + constexpr auto parse_format_specifiers(format_parse_context & context) -> format_specifiers + { + auto specifiers = format_specifiers{}; + auto it = context.begin(); + auto const end = context.end(); + + if (it != end && *it == '}') + { + return specifiers; + } + + if (std::next(it) != end && ((*std::next(it)) == '<' || (*std::next(it)) == '>' || (*std::next(it)) == '^')) + { + specifiers.fill = *it; + switch (*std::next(it)) + { + case '<': + specifiers.align = alignment::left; + break; + case '>': + specifiers.align = alignment::right; + break; + case '^': + default: + specifiers.align = alignment::center; + break; + } + std::advance(it, 2); + } + else if (*it == '<' || *it == '>' || *it == '^') + { + switch (*it) + { + case '<': + specifiers.align = alignment::left; + break; + case '>': + specifiers.align = alignment::right; + break; + case '^': + default: + specifiers.align = alignment::center; + break; + } + std::advance(it, 1); + } + + if (it != end && (*it == '+' || *it == '-' || *it == ' ')) + { + switch (*it) + { + case '+': + specifiers.sign = sign_mode::plus; + break; + case '-': + specifiers.sign = sign_mode::minus; + break; + case ' ': + default: + specifiers.sign = sign_mode::space; + break; + } + std::advance(it, 1); + } + + if (it != end && *it == '#') + { + specifiers.alternative_form = true; + std::advance(it, 1); + } + + if (it != end && *it == '0') + { + specifiers.zero_pad = true; + std::advance(it, 1); + } + + if (it != end && *it == '{') + { + specifiers.width_mode = width_mode::dynamic_argument_id; + std::advance(it, 1); + auto argument_id = 0uz; + + if (it != end && *it >= '0' && *it <= '9') + { + while (it != end && *it >= '0' && *it <= '9') + { + argument_id = argument_id * 10 + static_cast<std::size_t>(*it - '0'); + std::advance(it, 1); + } + context.check_arg_id(argument_id); + } + else + { + argument_id = context.next_arg_id(); + } + + if (it == end || *it != '}') + { + report_format_error("Expected '}' for dynamic width."); + } + std::advance(it, 1); + specifiers.width_value = argument_id; + } + else if (it != end && *it >= '0' && *it <= '9') + { + specifiers.width_mode = width_mode::static_value; + while (it != end && *it >= '0' && *it <= '9') + { + specifiers.width_value = specifiers.width_value * 10 + static_cast<std::size_t>(*it - '0'); + std::advance(it, 1); + } + } + + context.advance_to(it); + return specifiers; + } + + constexpr auto calculate_format_padding(std::size_t target_width, std::size_t content_length, + alignment requested_alignment, alignment default_alignment) -> format_padding + { + if (target_width <= content_length) + { + return {}; + } + + auto total_padding = target_width - content_length; + auto effective_alignment = (requested_alignment == alignment::none) ? default_alignment : requested_alignment; + + switch (effective_alignment) + { + case alignment::center: + { + auto left = total_padding / 2; + auto right = total_padding - left; + return {left, right}; + } + case alignment::left: + return {0, total_padding}; + case alignment::right: + default: + return {total_padding, 0}; + break; + } + } + +} // namespace kstd::bits + +#endif
\ No newline at end of file diff --git a/libs/kstd/include/kstd/bits/format_specs.hpp b/libs/kstd/include/kstd/bits/format_specs.hpp deleted file mode 100644 index 092a875..0000000 --- a/libs/kstd/include/kstd/bits/format_specs.hpp +++ /dev/null @@ -1,104 +0,0 @@ -#ifndef KSTD_BITS_FORMAT_SPECS_HPP -#define KSTD_BITS_FORMAT_SPECS_HPP - -// IWYU pragma: private - -#include <cstddef> -#include <iterator> -#include <string_view> - -namespace kstd::bits -{ - - struct format_specs - { - std::size_t width{}; - char fill{' '}; - char type{}; - bool align_left{}; - bool sign_plus{}; - bool sign_space{}; - bool alternative_form{}; - bool zero_pad{}; - }; - - constexpr auto parse_specs(std::string_view string, format_specs & specs) -> std::string_view - { - auto current = string.begin(); - auto end = string.end(); - - if (current == end || *current != ':') - { - return {current, end}; - } - - std::advance(current, 1); - - if (current != end && std::next(current) != end && (*std::next(current) == '<' || *std::next(current) == '>')) - { - specs.fill = *current; - specs.align_left = *std::next(current) == '<'; - std::advance(current, 2); - } - else if (current != end) - { - if (*current == '<') - { - specs.align_left = true; - std::advance(current, 1); - } - else if (*current == '>') - { - specs.align_left = false; - std::advance(current, 1); - } - } - - if (current != end) - { - if (*current == '+') - { - specs.sign_plus = true; - std::advance(current, 1); - } - else if (*current == ' ') - { - specs.sign_space = true; - std::advance(current, 1); - } - else if (*current == '-') - { - std::advance(current, 1); - } - } - - if (current != end && *current == '#') - { - specs.alternative_form = true; - std::advance(current, 1); - } - - if (current != end && *current == '0') - { - specs.zero_pad = true; - std::advance(current, 1); - } - - while (current != end && *current >= '0' && *current <= '9') - { - specs.width = specs.width * 10 + (*current - '0'); - std::advance(current, 1); - } - - if (current != end && *current != '}') - { - specs.type = *current; - std::advance(current, 1); - } - - return {current, end}; - } - -} // namespace kstd::bits - -#endif
\ No newline at end of file diff --git a/libs/kstd/include/kstd/bits/format_string.hpp b/libs/kstd/include/kstd/bits/format_string.hpp index 3a15bf0..cbdfb7d 100644 --- a/libs/kstd/include/kstd/bits/format_string.hpp +++ b/libs/kstd/include/kstd/bits/format_string.hpp @@ -3,127 +3,114 @@ // IWYU pragma: private, include <kstd/format> -#include <algorithm> +#include "kstd/bits/format_context.hpp" + #include <cstddef> -#include <iterator> #include <string_view> +#include <type_traits> namespace kstd { + template<typename> + struct formatter; + namespace bits { - auto invalid_format_string(char const *) -> void; - - consteval auto validate_format_string(std::string_view string, std::size_t argument_count) -> void + template<typename... Args> + constexpr auto validate_argument(std::size_t target_index, format_parse_context & context) -> void { - auto next_automatic_index = 0uz; - auto placeholder_count = 0uz; - auto has_manual_index = false; - auto has_automatic_index = false; + auto found = false; + auto current_index = 0uz; + + (..., [&] { + if (current_index == target_index && !found) + { + using decay_type = std::remove_cvref_t<Args>; + auto fmt = formatter<decay_type>{}; + + auto it = fmt.parse(context); + context.advance_to(it); + found = true; + } + ++current_index; + }()); + + if (!found) + { + report_format_error("Argument index out of bounds."); + } + } + } // namespace bits - auto current = string.begin(); - auto end = string.end(); + template<typename... Args> + struct format_string + { + template<std::size_t Size> + consteval format_string(char const (&str)[Size]) noexcept(false) // NOLINT + : str_view{str} + { + auto context = format_parse_context{str_view, sizeof...(Args)}; + auto it = context.begin(); - while (current != end) + while (it != context.end()) { - if (*current == '{') + if (*it == '{') { - if (std::next(current) != end && *std::next(current) == '{') + ++it; + if (it != context.end() && *it == '{') { - std::advance(current, 2); + ++it; + context.advance_to(it); continue; } - std::advance(current, 1); - - auto index = 0uz; - auto is_manual_index = false; + context.advance_to(it); + auto argument_id = 0uz; - if (current != end && *current >= '0' && *current <= '9') + if (it != context.end() && *it >= '0' && *it <= '9') { - is_manual_index = true; - while (current != end && *current >= '0' && *current <= '9') + while (it != context.end() && *it >= '0' && *it <= '9') { - index = index * 10 + (*current - '0'); - std::advance(current, 1); - } - } - - if (is_manual_index) - { - placeholder_count = std::max(placeholder_count, index + 1); - if (has_automatic_index) - { - invalid_format_string("Cannot mix automatic and manual indexing."); - } - has_manual_index = true; - if (index >= argument_count) - { - invalid_format_string("Argument index out of range"); + argument_id = argument_id * 10 + static_cast<std::size_t>(*it - '0'); + ++it; } + context.check_arg_id(argument_id); + context.advance_to(it); } else { - if (has_manual_index) - { - invalid_format_string("Cannot mix automatic and manual indexing."); - } - has_automatic_index = true; - ++placeholder_count; - if (next_automatic_index >= argument_count) - { - invalid_format_string("Not enough arguments provided for format string."); - } - index = next_automatic_index++; + argument_id = context.next_arg_id(); } - while (current != end && *current != '}') + if (it != context.end() && *it == ':') { - std::advance(current, 1); + ++it; + context.advance_to(it); } - if (current == end) + bits::validate_argument<Args...>(argument_id, context); + + it = context.begin(); + if (it == context.end() || *it != '}') { - invalid_format_string("Unexpected end of format string."); + report_format_error("Missing closing '}' in format string."); } - std::advance(current, 1); } - else if (*current == '}') + else if (*it == '}') { - if (std::next(current) != end && *std::next(current) == '}') + ++it; + if (it != context.end() && *it == '}') { - std::advance(current, 2); - continue; + report_format_error("Unescaped '}' in format string."); } - invalid_format_string("Unexpected '}' in format string."); - } - else - { - std::advance(current, 1); } - } - if (argument_count < placeholder_count) - { - invalid_format_string("Not enough arguments provided for format string."); - } - else if (argument_count > placeholder_count) - { - invalid_format_string("Too many arguments provided for format string."); + ++it; + context.advance_to(it); } } - } // namespace bits - - template<typename... Args> - struct format_string - { - std::string_view str; - consteval format_string(char const * format) - : str{format} - { - bits::validate_format_string(str, sizeof...(Args)); - } + std::string_view str_view; }; } // namespace kstd diff --git a/libs/kstd/include/kstd/bits/formatter.hpp b/libs/kstd/include/kstd/bits/formatter.hpp index 7c9c31d..e9a45e0 100644 --- a/libs/kstd/include/kstd/bits/formatter.hpp +++ b/libs/kstd/include/kstd/bits/formatter.hpp @@ -3,8 +3,9 @@ // IWYU pragma: private, include <kstd/format> -#include <kstd/bits/format_context.hpp> -#include <kstd/bits/format_specs.hpp> +#include "kstd/bits/format_context.hpp" +#include "kstd/bits/format_specifiers.hpp" + #include <kstd/os/error.hpp> #include <array> @@ -21,30 +22,98 @@ namespace kstd { - template<typename T> + template<typename> struct formatter; + template<> + struct formatter<std::string_view> + { + constexpr auto parse(format_parse_context & context) -> format_parse_context::iterator + { + auto it = context.begin(); + + if (it != context.end() && *it == 's') + { + ++it; + } + + if (it != context.end() && *it != '}') + { + report_format_error("Invalid specifier for string_view."); + } + return it; + } + + auto format(std::string_view const & string, format_context & context) const -> void + { + context.push(string); + } + }; + + template<> + struct formatter<char const *> : formatter<std::string_view> + { + auto format(char const * string, format_context & context) const -> void + { + formatter<std::string_view>::format(string ? std::string_view{string} : "(null)", context); + } + }; + template<std::integral T> struct formatter<T> { - bits::format_specs specs{}; + bits::format_specifiers specifiers{}; + + constexpr auto static maximum_digits = 80; - constexpr auto parse(std::string_view context) -> std::string_view + enum struct base { - return bits::parse_specs(context, specs); + bin = 2, + oct = 8, + dec = 10, + hex = 16, + }; + + constexpr auto parse(format_parse_context & context) -> format_parse_context::iterator + { + specifiers = bits::parse_format_specifiers(context); + + auto it = context.begin(); + auto const end = context.end(); + + if (it != end && *it != '}') + { + if (*it == 'b' || *it == 'B' || *it == 'd' || *it == 'o' || *it == 'x' || *it == 'X' || *it == 'p') + { + specifiers.type = *it; + std::advance(it, 1); + } + else + { + report_format_error("Invalid type specifier for integral type."); + } + } + + if (it != end && *it != '}') + { + report_format_error("Missing terminating '}' in format string."); + } + + return it; } auto format(T value, format_context & context) const -> void { - enum struct base + auto final_width = 0uz; + if (specifiers.width_mode == bits::width_mode::static_value) { - bin = 2, - oct = 8, - dec = 10, - hex = 16, - }; - - constexpr auto static maximum_digits = 80; + final_width = specifiers.width_value; + } + else if (specifiers.width_mode == bits::width_mode::dynamic_argument_id) + { + // TODO: implement argument references so we can read the dynamic width argument. + final_width = 0; + } using unsigned_T = std::make_unsigned_t<T>; auto absolute_value = static_cast<unsigned_T>(value); @@ -55,11 +124,11 @@ namespace kstd if (value < 0) { is_negative = true; - absolute_value = 0 - value; + absolute_value = 0 - static_cast<unsigned_T>(value); } } - auto const base = [type = specs.type] -> auto { + auto const base = [type = specifiers.type] -> auto { switch (type) { case 'x': @@ -77,7 +146,7 @@ namespace kstd }(); auto buffer = std::array<char, maximum_digits>{}; - auto digits = (specs.type == 'X') ? "0123456789ABCDEF" : "0123456789abcdef"; + auto digits = (specifiers.type == 'X') ? "0123456789ABCDEF" : "0123456789abcdef"; auto current = buffer.rbegin(); if (absolute_value == 0) @@ -95,21 +164,22 @@ namespace kstd } } + auto content_length = static_cast<std::size_t>(std::distance(buffer.rbegin(), current)); auto prefix = std::array<char, 2>{'0', '\0'}; auto prefix_length = 0uz; - if (specs.alternative_form) + if (specifiers.alternative_form) { switch (base) { case base::bin: - prefix[1] = specs.type == 'B' ? 'B' : 'b'; + prefix[1] = specifiers.type == 'B' ? 'B' : 'b'; prefix_length = 2; break; case base::oct: prefix_length = 1; break; case base::hex: - prefix[1] = specs.type == 'X' ? 'X' : 'x'; + prefix[1] = specifiers.type == 'X' ? 'X' : 'x'; prefix_length = 2; break; default: @@ -122,53 +192,50 @@ namespace kstd { sign_character = '-'; } - else if (specs.sign_plus) + else if (specifiers.sign == bits::sign_mode::plus) { sign_character = '+'; } - else if (specs.sign_space) + else if (specifiers.sign == bits::sign_mode::space) { sign_character = ' '; } - auto const content_length = static_cast<std::size_t>(std::distance(buffer.rbegin(), current)); auto const total_length = content_length + prefix_length + (sign_character != '\0'); - auto const padding_length = (specs.width > total_length) ? (specs.width - total_length) : 0; + auto const padding = + bits::calculate_format_padding(final_width, total_length, specifiers.align, bits::alignment::right); + auto const effective_zero_pad = specifiers.zero_pad && (specifiers.align == bits::alignment::none); - if (!specs.align_left && !specs.zero_pad) + if (!effective_zero_pad) { - for (auto i = 0uz; i < padding_length; ++i) + for (a |
