#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, .args = args}; auto parse_context = kstd::format_parse_context{format, args.size()}; auto it = parse_context.begin(); auto end = parse_context.end(); while (it != end) { if (*it != '{' && *it != '}') { auto start = it; while (it != end && *it != '{' && *it != '}') { std::advance(it, 1); } parse_context.advance_to(it); context.push(std::string_view(start, it - start)); continue; } if (*it == '{') { std::advance(it, 1); if (it != end && *it == '{') { context.push('{'); std::advance(it, 1); parse_context.advance_to(it); continue; } parse_context.advance_to(it); auto index = 0uz; if (it != end && *it >= '0' && *it <= '9') { while (it != end && *it >= '0' && *it <= '9') { index = index * 10 + static_cast(*it - '0'); std::advance(it, 1); } parse_context.check_arg_id(index); } else { index = parse_context.next_arg_id(); } if (it != end && *it == ':') { std::advance(it, 1); } parse_context.advance_to(it); if (index < args.size()) { auto const & arg = args[index]; switch (arg.type) { case kstd::bits::format::arg_type::boolean: { auto fmt = kstd::formatter{}; auto const parsed = fmt.parse(parse_context); parse_context.advance_to(parsed); fmt.format(arg.value.boolean, context); break; } case kstd::bits::format::arg_type::character: { auto fmt = kstd::formatter{}; auto const parsed = fmt.parse(parse_context); parse_context.advance_to(parsed); fmt.format(arg.value.character, context); break; } case kstd::bits::format::arg_type::integer: { auto fmt = kstd::formatter{}; auto const parsed = fmt.parse(parse_context); parse_context.advance_to(parsed); fmt.format(arg.value.integer, context); break; } case kstd::bits::format::arg_type::unsigned_integer: { auto fmt = kstd::formatter{}; auto const parsed = fmt.parse(parse_context); parse_context.advance_to(parsed); fmt.format(arg.value.unsigned_integer, context); break; } case kstd::bits::format::arg_type::string_view: { auto fmt = kstd::formatter{}; auto const parsed = fmt.parse(parse_context); parse_context.advance_to(parsed); fmt.format(arg.value.string_view, context); break; } case kstd::bits::format::arg_type::c_string: { auto fmt = kstd::formatter{}; auto const parsed = fmt.parse(parse_context); parse_context.advance_to(parsed); fmt.format(arg.value.c_string, context); break; } case kstd::bits::format::arg_type::pointer: { auto fmt = kstd::formatter{}; auto const parsed = fmt.parse(parse_context); parse_context.advance_to(parsed); fmt.format(arg.value.pointer, context); break; } case kstd::bits::format::arg_type::user_defined: { if (arg.value.user_defined.format) { arg.value.user_defined.format(arg.value.user_defined.pointer, parse_context, context); } else { context.push("{?}"); } break; } default: { context.push("{fmt-err: unknown-type}"); break; } } } 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 if (*it == '}') { 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); } } } } } // namespace kstd::os