#include "kapi/cio.hpp" #include #include #include #include #include #include #include namespace kstd::os { namespace { struct write_buffer { using output_stream = kapi::cio::output_stream; constexpr auto static size = 128uz; write_buffer(write_buffer const &) = delete; write_buffer(write_buffer &&) = delete; auto operator=(write_buffer const &) -> write_buffer & = delete; auto operator=(write_buffer &&) -> write_buffer & = delete; explicit write_buffer(output_stream stream) : m_stream{stream} {} ~write_buffer() noexcept { flush(); } auto flush() noexcept -> void { if (m_position > 0) { std::string_view chunk{m_buffer.data(), m_position}; kapi::cio::write(m_stream, chunk); m_position = 0; } } auto static callback(void * object, std::string_view text) -> void { auto * self = static_cast(object); for (char const character : text) { if (self->m_position >= size) { self->flush(); } self->m_buffer.at(self->m_position++) = character; } } private: output_stream m_stream; std::array m_buffer{}; std::size_t m_position{}; }; } // namespace auto vprint(print_sink sink, std::string_view format, kstd::format_args args) -> void { 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 current = format.begin(); auto end = format.end(); auto next_automatic_index = 0uz; while (current != end) { if (*current != '{') { auto start = current; while (current != end && *current != '{') { std::advance(current, 1); } context.push(std::string_view(start, current - start)); continue; } if (std::next(current) != end && *(std::next(current)) == '{') { context.push('{'); std::advance(current, 2); continue; } std::advance(current, 1); auto index = 0uz; if (current != end && *current >= '0' && *current <= '9') { while (current != end && *current >= '0' && *current <= '9') { index = index * 10 + static_cast(*current - '0'); std::advance(current, 1); } } else { index = next_automatic_index++; } auto remaining_fmt = std::string_view{current, static_cast(std::distance(current, end))}; 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); } if (current != end && *current == '}') { std::advance(current, 1); } else { context.push("{fmt-err}"); while (current != end && *current != '}') std::advance(current, 1); if (current != end) std::advance(current, 1); } } } } // namespace kstd::os