aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFelix Morgner <felix.morgner@ost.ch>2025-12-19 11:46:46 +0100
committerFelix Morgner <felix.morgner@ost.ch>2025-12-19 11:46:46 +0100
commitde96b0588ab680e1002c12df7ea7900d7eb71cf8 (patch)
tree41728f4f5c77a4d96dc3d89096483dfee75b3482
parent266dde7d535d997a45f6eef41e44ebcaa516b75a (diff)
downloadteachos-de96b0588ab680e1002c12df7ea7900d7eb71cf8.tar.xz
teachos-de96b0588ab680e1002c12df7ea7900d7eb71cf8.zip
kstd: move println to kstd
-rw-r--r--arch/x86_64/include/x86_64/vga/text.hpp20
-rw-r--r--arch/x86_64/src/kapi/memory.cpp9
-rw-r--r--arch/x86_64/src/memory/kernel_mapper.cpp5
-rw-r--r--arch/x86_64/src/vga/text.cpp32
-rw-r--r--kapi/include/kapi/cio.hpp100
-rw-r--r--kapi/include/kapi/cio/output_device.hpp24
-rw-r--r--kernel/CMakeLists.txt5
-rw-r--r--kernel/src/kapi/cio.cpp183
-rw-r--r--kernel/src/kapi/system.cpp7
-rw-r--r--kernel/src/kstd/os.cpp (renamed from kernel/src/kstd.cpp)0
-rw-r--r--kernel/src/kstd/print.cpp145
-rw-r--r--kernel/src/main.cpp6
-rw-r--r--libs/kstd/include/kstd/print86
13 files changed, 313 insertions, 309 deletions
diff --git a/arch/x86_64/include/x86_64/vga/text.hpp b/arch/x86_64/include/x86_64/vga/text.hpp
index 9f80f94..fcda67f 100644
--- a/arch/x86_64/include/x86_64/vga/text.hpp
+++ b/arch/x86_64/include/x86_64/vga/text.hpp
@@ -121,25 +121,7 @@ namespace teachos::vga::x86_64::text
*/
auto cursor(bool enabled) -> void;
- auto write(std::string_view text) -> void override
- {
- write(text, common_attributes::green_on_black);
- }
-
- auto writeln(std::string_view text) -> void override
- {
- writeln(text, common_attributes::green_on_black);
- }
-
- auto write_error(std::string_view text) -> void override
- {
- write(text, common_attributes::red_on_black);
- }
-
- auto writeln_error(std::string_view text) -> void override
- {
- writeln(text, common_attributes::red_on_black);
- }
+ auto write(cio::output_stream stream, std::string_view text) -> void override;
private:
using glyph = std::pair<char, std::byte>;
diff --git a/arch/x86_64/src/kapi/memory.cpp b/arch/x86_64/src/kapi/memory.cpp
index 0a45a51..5234110 100644
--- a/arch/x86_64/src/kapi/memory.cpp
+++ b/arch/x86_64/src/kapi/memory.cpp
@@ -1,7 +1,6 @@
#include "kapi/memory.hpp"
#include "kapi/boot.hpp"
-#include "kapi/cio.hpp"
#include "kapi/system.hpp"
#include "x86_64/boot/boot.hpp"
@@ -17,6 +16,8 @@
#include "x86_64/memory/region_allocator.hpp"
#include "x86_64/memory/scoped_mapping.hpp"
+#include <kstd/print>
+
#include <multiboot2/information.hpp>
#include <atomic>
@@ -164,7 +165,7 @@ namespace teachos::memory
system::panic("[x86_64] Memory management has already been initialized.");
}
- cio::println("[x86_64:MEM] Enabling additional CPU protection features.");
+ kstd::println("[x86_64:MEM] Enabling additional CPU protection features.");
enable_cpu_protections();
@@ -172,7 +173,7 @@ namespace teachos::memory
buffered_allocator.emplace(&*region_based_allocator);
recursive_page_mapper.emplace(*buffered_allocator);
- cio::println("[x86_64:MEM] Preparing new paging hierarchy.");
+ kstd::println("[x86_64:MEM] Preparing new paging hierarchy.");
auto new_pml4_frame = inject_faux_pml4(*buffered_allocator, *recursive_page_mapper);
@@ -180,7 +181,7 @@ namespace teachos::memory
remap_vga_text_mode_buffer(*recursive_page_mapper);
remap_multiboot_information(*recursive_page_mapper);
- cio::println("[x86_64:MEM] Switching to new paging hierarchy.");
+ kstd::println("[x86_64:MEM] Switching to new paging hierarchy.");
auto cr3 = cpu::x86_64::cr3::read();
cr3.frame(new_pml4_frame);
diff --git a/arch/x86_64/src/memory/kernel_mapper.cpp b/arch/x86_64/src/memory/kernel_mapper.cpp
index 5295bb3..50fa325 100644
--- a/arch/x86_64/src/memory/kernel_mapper.cpp
+++ b/arch/x86_64/src/memory/kernel_mapper.cpp
@@ -1,11 +1,12 @@
#include "x86_64/memory/kernel_mapper.hpp"
-#include "kapi/cio.hpp"
#include "kapi/memory.hpp"
#include "kapi/system.hpp"
#include "x86_64/boot/ld.hpp"
+#include <kstd/print>
+
#include <elf/format.hpp>
#include <elf/section_header.hpp>
#include <multiboot2/information.hpp>
@@ -70,7 +71,7 @@ namespace teachos::memory::x86_64
auto kernel_mapper::map_section(section_header_type const & section, std::string_view name, page_mapper & mapper)
-> void
{
- cio::println("[x86_64:MEM] mapping {} ({} bytes)", name, section.size);
+ kstd::println("[x86_64:MEM] mapping {} ({} bytes)", name, section.size);
auto number_of_pages = (section.size + (PLATFORM_PAGE_SIZE - 1)) / PLATFORM_PAGE_SIZE;
diff --git a/arch/x86_64/src/vga/text.cpp b/arch/x86_64/src/vga/text.cpp
index d4548a2..8f6214e 100644
--- a/arch/x86_64/src/vga/text.cpp
+++ b/arch/x86_64/src/vga/text.cpp
@@ -1,6 +1,7 @@
#include "x86_64/vga/text.hpp"
#include "kapi/boot.hpp"
+#include "kapi/cio.hpp"
#include "x86_64/boot/boot.hpp"
#include "x86_64/boot/ld.hpp"
@@ -76,6 +77,20 @@ namespace teachos::vga::x86_64::text
m_position = (line() - scroll_count) * DEFAULT_TEXT_BUFFER_WIDTH;
}
+ auto device::write(cio::output_stream stream, std::string_view text) -> void
+ {
+ auto attributes = [&] -> attribute {
+ switch (stream)
+ {
+ case cio::output_stream::stderr:
+ return common_attributes::red_on_black;
+ default:
+ return common_attributes::green_on_black;
+ }
+ }();
+ write(text, attributes);
+ }
+
auto device::write(std::string_view code_points, attribute attribute) -> void
{
std::ranges::for_each(code_points, [&](auto code_point) -> void { write(code_point, attribute); });
@@ -87,16 +102,17 @@ namespace teachos::vga::x86_64::text
{
scroll();
}
- buffer[m_position++] = std::pair{code_point, std::bit_cast<std::byte>(attribute)};
- };
- auto device::writeln(std::string_view code_points, attribute attribute) -> void
- {
- std::ranges::for_each(code_points, [&](auto code_point) -> void { write(code_point, attribute); });
- if (column())
+ if (code_point == '\n')
{
- newline();
+ if (column())
+ {
+ newline();
+ }
+ return;
}
- }
+
+ buffer[m_position++] = std::pair{code_point, std::bit_cast<std::byte>(attribute)};
+ };
} // namespace teachos::vga::x86_64::text
diff --git a/kapi/include/kapi/cio.hpp b/kapi/include/kapi/cio.hpp
index 98c715b..30619ec 100644
--- a/kapi/include/kapi/cio.hpp
+++ b/kapi/include/kapi/cio.hpp
@@ -5,10 +5,8 @@
#include <kstd/format>
-#include <array>
#include <optional>
#include <string_view>
-#include <type_traits>
namespace teachos::cio
{
@@ -27,79 +25,31 @@ namespace teachos::cio
//! @return The previously active output device.
auto set_output_device(output_device & device) -> std::optional<output_device *>;
- //! @qualifier kernel-defined
- //! Print the given text to the currently active output device.
- //!
- //! @param text The text to print.
- auto print(std::string_view text) -> void;
-
- //! @qualifier kernel-defined
- //! Print the given text, including a newline, to the currently active output device.
- //!
- //! @param text The text to print.
- auto println(std::string_view text) -> void;
-
- //! @qualifier kernel-defined
- //! Print the given error text, to the currently active output device.
- //!
- //! @param text The error text to print.
- auto print_error(std::string_view text) -> void;
-
- //! @qualifier kernel-defined
- //! Print the given error text, including a newline, to the currently active output device.
- //!
- //! @param text The error text to print.
- auto println_error(std::string_view text) -> void;
-
- template<typename... Args>
- // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward)
- auto print(kstd::format_string<std::type_identity_t<Args>...> format, Args &&... args) -> void
- {
- auto vprint(std::string_view format, kstd::format_args args) -> void;
- auto arguments = std::array{
- kstd::format_arg{&args, kstd::format_dispatcher<std::remove_cvref_t<Args>>}
- ...
- };
- vprint(format.str, kstd::format_args{arguments.data(), sizeof...(Args)});
- }
-
- template<typename... Args>
- // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward)
- auto println(kstd::format_string<std::type_identity_t<Args>...> format, Args &&... args) -> void
- {
- auto vprint(std::string_view format, kstd::format_args args) -> void;
- auto arguments = std::array{
- kstd::format_arg{&args, kstd::format_dispatcher<std::remove_cvref_t<Args>>}
- ...
- };
- vprint(format.str, kstd::format_args{arguments.data(), sizeof...(Args)});
- println("");
- }
-
- template<typename... Args>
- // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward)
- auto print_error(kstd::format_string<std::type_identity_t<Args>...> format, Args &&... args) -> void
- {
- auto vprint_error(std::string_view format, kstd::format_args args) -> void;
- auto arguments = std::array{
- kstd::format_arg{&args, kstd::format_dispatcher<std::remove_cvref_t<Args>>}
- ...
- };
- vprint_error(format.str, kstd::format_args{arguments.data(), sizeof...(Args)});
- }
-
- template<typename... Args>
- // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward)
- auto println_error(kstd::format_string<std::type_identity_t<Args>...> format, Args &&... args) -> void
- {
- auto vprint_error(std::string_view format, kstd::format_args args) -> void;
- auto arguments = std::array{
- kstd::format_arg{&args, kstd::format_dispatcher<std::remove_cvref_t<Args>>}
- ...
- };
- vprint_error(format.str, kstd::format_args{arguments.data(), sizeof...(Args)});
- println_error("");
- }
+ auto write(output_stream stream, std::string_view text) -> void;
+
+ // //! @qualifier kernel-defined
+ // //! Print the given text to the currently active output device.
+ // //!
+ // //! @param text The text to print.
+ // auto print(std::string_view text) -> void;
+
+ // //! @qualifier kernel-defined
+ // //! Print the given text, including a newline, to the currently active output device.
+ // //!
+ // //! @param text The text to print.
+ // auto println(std::string_view text) -> void;
+
+ // //! @qualifier kernel-defined
+ // //! Print the given error text, to the currently active output device.
+ // //!
+ // //! @param text The error text to print.
+ // auto print_error(std::string_view text) -> void;
+
+ // //! @qualifier kernel-defined
+ // //! Print the given error text, including a newline, to the currently active output device.
+ // //!
+ // //! @param text The error text to print.
+ // auto println_error(std::string_view text) -> void;
} // namespace teachos::cio
diff --git a/kapi/include/kapi/cio/output_device.hpp b/kapi/include/kapi/cio/output_device.hpp
index 0599906..bbdf6ed 100644
--- a/kapi/include/kapi/cio/output_device.hpp
+++ b/kapi/include/kapi/cio/output_device.hpp
@@ -8,6 +8,12 @@
namespace teachos::cio
{
+ enum struct output_stream
+ {
+ stdout,
+ stderr,
+ };
+
//! The interface of a device able to perform character output on a platform.
struct output_device
{
@@ -20,23 +26,9 @@ namespace teachos::cio
//! Write the given text to the output device.
//!
+ //! @param stream The stream to write to.
//! @param text The text to write.
- auto virtual write(std::string_view text) -> void = 0;
-
- //! Write the given text to the output device, appending a newline
- //!
- //! @param text The text to write.
- auto virtual writeln(std::string_view text) -> void = 0;
-
- //! Write the given error text to the output device.
- //!
- //! @param text The text to write.
- auto virtual write_error(std::string_view text) -> void = 0;
-
- //! Write the given error text to the output device, appending a newline
- //!
- //! @param text The text to write.
- auto virtual writeln_error(std::string_view text) -> void = 0;
+ auto virtual write(output_stream stream, std::string_view text) -> void = 0;
protected:
output_device() = default;
diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt
index 6bddf7c..7733c1b 100644
--- a/kernel/CMakeLists.txt
+++ b/kernel/CMakeLists.txt
@@ -1,7 +1,10 @@
add_executable("kernel"
- "src/kstd.cpp"
"src/main.cpp"
+ # KSTD OS Implementation
+ "src/kstd/os.cpp"
+ "src/kstd/print.cpp"
+
# Platform Independent KAPI implementation
"src/kapi/cio.cpp"
"src/kapi/memory.cpp"
diff --git a/kernel/src/kapi/cio.cpp b/kernel/src/kapi/cio.cpp
index 500d61e..fd5ad0d 100644
--- a/kernel/src/kapi/cio.cpp
+++ b/kernel/src/kapi/cio.cpp
@@ -1,10 +1,5 @@
#include "kapi/cio.hpp"
-#include <kstd/format>
-
-#include <array>
-#include <cstddef>
-#include <iterator>
#include <optional>
#include <string_view>
#include <utility>
@@ -16,72 +11,10 @@ namespace teachos::cio
struct null_device final : public output_device
{
null_device static instance;
-
- auto write(std::string_view) -> void override {}
- auto writeln(std::string_view) -> void override {}
-
- auto write_error(std::string_view) -> void override {}
- auto writeln_error(std::string_view) -> void override {}
+ auto write(output_stream, std::string_view) -> void override {}
};
constinit null_device null_device::instance;
-
- struct write_buffer
- {
- 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_device * device, bool write_to_error)
- : m_device{device}
- , m_write_to_error{write_to_error}
- {}
-
- ~write_buffer() noexcept
- {
- flush();
- }
-
- auto flush() noexcept -> void
- {
- if (m_position > 0)
- {
- std::string_view chunk{m_buffer.data(), m_position};
- if (m_write_to_error)
- {
- m_device->write_error(chunk);
- }
- else
- {
- m_device->write(chunk);
- }
- m_position = 0;
- }
- }
-
- auto static callback(void * object, std::string_view text) -> void
- {
- auto * self = static_cast<write_buffer *>(object);
- for (char const character : text)
- {
- if (self->m_position >= size)
- {
- self->flush();
- }
- self->m_buffer.at(self->m_position++) = character;
- }
- }
-
- private:
- output_device * m_device;
- bool m_write_to_error;
- std::array<char, size> m_buffer{};
- std::size_t m_position{};
- };
-
} // namespace
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
@@ -96,117 +29,9 @@ namespace teachos::cio
return std::exchange(active_device, &device);
}
- auto print(std::string_view text) -> void
- {
- active_device->write(text);
- }
-
- auto println(std::string_view text) -> void
- {
- active_device->writeln(text);
- }
-
- auto print_error(std::string_view text) -> void
- {
- active_device->write_error(text);
- }
-
- auto println_error(std::string_view text) -> void
- {
- active_device->writeln_error(text);
- }
-
- namespace
- {
- auto static vprint_impl(std::string_view fmt, kstd::format_args args, bool write_to_error) -> void
- {
- if (!active_device)
- return;
-
- auto writer = write_buffer{active_device, write_to_error};
- auto context = kstd::format_context{.writer = write_buffer::callback, .user_data = &writer};
-
- auto current = fmt.begin();
- auto end = fmt.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')
- {
- // NOLINTNEXTLINE(cppcoreguidelines-avoid-magic-numbers)
- index = index * 10 + static_cast<std::size_t>(*current - '0');
- std::advance(current, 1);
- }
- }
- else
- {
- index = next_automatic_index++;
- }
-
- auto remaining_fmt = std::string_view{current, static_cast<std::size_t>(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
-
- auto vprint(std::string_view fmt, kstd::format_args args) -> void
- {
- vprint_impl(fmt, args, false);
- }
-
- auto vprint_error(std::string_view fmt, kstd::format_args args) -> void
+ auto write(output_stream stream, std::string_view text) -> void
{
- vprint_impl(fmt, args, true);
+ active_device->write(stream, text);
}
-} // namespace teachos::cio \ No newline at end of file
+} // namespace teachos::cio
diff --git a/kernel/src/kapi/system.cpp b/kernel/src/kapi/system.cpp
index 1cca82e..cdde049 100644
--- a/kernel/src/kapi/system.cpp
+++ b/kernel/src/kapi/system.cpp
@@ -1,8 +1,9 @@
#include "kapi/system.hpp"
-#include "kapi/cio.hpp"
#include "kapi/cpu.hpp"
+#include <kstd/print>
+
#include <source_location>
#include <string_view>
@@ -12,8 +13,8 @@ namespace teachos::system
[[gnu::weak]]
auto panic(std::string_view message, std::source_location location) -> void
{
- cio::println_error("[PANIC] in {} : {} @ {}:{}", location.function_name(), message, location.file_name(),
- location.line());
+ kstd::println(kstd::print_sink::stderr, "[PANIC] in {} : {} @ {}:{}", location.function_name(), message,
+ location.file_name(), location.line());
cpu::halt();
}
diff --git a/kernel/src/kstd.cpp b/kernel/src/kstd/os.cpp
index 41c4d60..41c4d60 100644
--- a/kernel/src/kstd.cpp
+++ b/kernel/src/kstd/os.cpp
diff --git a/kernel/src/kstd/print.cpp b/kernel/src/kstd/print.cpp
new file mode 100644
index 0000000..d2fdafa
--- /dev/null
+++ b/kernel/src/kstd/print.cpp
@@ -0,0 +1,145 @@
+#include "kapi/cio.hpp"
+
+#include <kstd/format>
+#include <kstd/print>
+
+#include <array>
+#include <cstddef>
+#include <iterator>
+#include <string_view>
+
+namespace kstd::os
+{
+
+ namespace
+ {
+ struct write_buffer
+ {
+ using output_stream = teachos::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};
+ teachos::cio::write(m_stream, chunk);
+ m_position = 0;
+ }
+ }
+
+ auto static callback(void * object, std::string_view text) -> void
+ {
+ auto * self = static_cast<write_buffer *>(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<char, size> 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) ? teachos::cio::output_stream::stderr
+ : teachos::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')
+ {
+ // NOLINTNEXTLINE(cppcoreguidelines-avoid-magic-numbers)
+ index = index * 10 + static_cast<std::size_t>(*current - '0');
+ std::advance(current, 1);
+ }
+ }
+ else
+ {
+ index = next_automatic_index++;
+ }
+
+ auto remaining_fmt = std::string_view{current, static_cast<std::size_t>(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
diff --git a/kernel/src/main.cpp b/kernel/src/main.cpp
index 3394275..f1e5dd0 100644
--- a/kernel/src/main.cpp
+++ b/kernel/src/main.cpp
@@ -2,13 +2,15 @@
#include "kapi/memory.hpp"
#include "kapi/system.hpp"
+#include <kstd/print>
+
auto main() -> int
{
teachos::cio::init();
- teachos::cio::println("[OS] IO subsystem initialized.");
+ kstd::println("[OS] IO subsystem initialized.");
teachos::memory::init();
- teachos::cio::println("[OS] Memory subsystem initialized.");
+ kstd::println("[OS] Memory subsystem initialized.");
teachos::system::panic("Returning from kernel main!");
}
diff --git a/libs/kstd/include/kstd/print b/libs/kstd/include/kstd/print
new file mode 100644
index 0000000..df42997
--- /dev/null
+++ b/libs/kstd/include/kstd/print
@@ -0,0 +1,86 @@
+#ifndef KSTD_PRINT
+#define KSTD_PRINT
+
+#include <kstd/format>
+
+#include <array>
+#include <string_view>
+#include <type_traits>
+
+namespace kstd
+{
+
+ enum struct print_sink
+ {
+ stdout,
+ stderr,
+ };
+
+ namespace os
+ {
+ auto vprint(print_sink sink, std::string_view format, kstd::format_args args) -> void;
+ } // namespace os
+
+ //! @qualifier kernel-defined
+ //! Format the given string using the given arguments and print it to the currently active output device.
+ //!
+ //! @param format The format string
+ //! @param args The arguments to use to place in the format string's placeholders.
+ template<typename... Args>
+ // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward)
+ auto print(kstd::format_string<std::type_identity_t<Args>...> format, Args &&... args) -> void
+ {
+ auto arguments = std::array<kstd::format_arg, sizeof...(Args)>{
+ kstd::format_arg{&args, kstd::format_dispatcher<std::remove_cvref_t<Args>>}
+ ...
+ };
+ os::vprint(print_sink::stdout, format.str, kstd::format_args{arguments.data(), sizeof...(Args)});
+ }
+
+ //! @qualifier kernel-defined
+ //! Format the given error string using the given arguments and print it to the currently active output device.
+ //!
+ //! @param format The format string
+ //! @param args The arguments to use to place in the format string's placeholders.
+ template<typename... Args>
+ // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward)
+ auto print(print_sink sink, kstd::format_string<std::type_identity_t<Args>...> format, Args &&... args) -> void
+ {
+ auto arguments = std::array<kstd::format_arg, sizeof...(Args)>{
+ kstd::format_arg{&args, kstd::format_dispatcher<std::remove_cvref_t<Args>>}
+ ...
+ };
+ os::vprint(sink, format.str, kstd::format_args{arguments.data(), sizeof...(Args)});
+ }
+
+ //! @qualifier kernel-defined
+ //! Format the given string using the given arguments and print it, including a newline, to the currently active
+ //! output device.
+ //!
+ //! @param format The format string
+ //! @param args The arguments to use to place in the format string's placeholders.
+ template<typename... Args>
+ // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward)
+ auto println(kstd::format_string<std::type_identity_t<Args>...> format, Args &&... args) -> void
+ {
+ print(format, std::forward<Args>(args)...);
+ print(print_sink::stdout, "\n");
+ }
+
+ //! @qualifier kernel-defined
+ //! Format the given error string using the given arguments and print it, including a newline, to the currently active
+ //! output device.
+ //!
+ //! @param format The format string
+ //! @param args The arguments
+ template<typename... Args>
+ // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward)
+ auto println(print_sink sink, kstd::format_string<std::type_identity_t<Args>...> format, Args &&... args) -> void
+ {
+ print(sink, format, std::forward<Args>(args)...);
+ print(sink, "\n");
+ }
+
+} // namespace kstd
+
+#endif \ No newline at end of file