aboutsummaryrefslogtreecommitdiff
path: root/libs
diff options
context:
space:
mode:
authorFelix Morgner <felix.morgner@ost.ch>2026-03-20 14:03:45 +0100
committerFelix Morgner <felix.morgner@ost.ch>2026-03-20 14:03:45 +0100
commit07cb15c42c16497b0b09b75886ce3baddeaaafb3 (patch)
treed2c4d91e6f55b6c1694f0ddeb89a457e8061c3e1 /libs
parent8365a09e18ac043d2e0aa6a9d3e394a02fe7c6cb (diff)
downloadteachos-07cb15c42c16497b0b09b75886ce3baddeaaafb3.tar.xz
teachos-07cb15c42c16497b0b09b75886ce3baddeaaafb3.zip
kstd/format: split implementation
Diffstat (limited to 'libs')
-rw-r--r--libs/kstd/include/kstd/bits/format/arg.hpp26
-rw-r--r--libs/kstd/include/kstd/bits/format/args.hpp75
-rw-r--r--libs/kstd/include/kstd/bits/format/context.hpp61
-rw-r--r--libs/kstd/include/kstd/bits/format/error.hpp24
-rw-r--r--libs/kstd/include/kstd/bits/format/formatter.hpp14
-rw-r--r--libs/kstd/include/kstd/bits/format/formatter/bool.hpp83
-rw-r--r--libs/kstd/include/kstd/bits/format/formatter/cstring.hpp29
-rw-r--r--libs/kstd/include/kstd/bits/format/formatter/integral.hpp204
-rw-r--r--libs/kstd/include/kstd/bits/format/formatter/ordering.hpp111
-rw-r--r--libs/kstd/include/kstd/bits/format/formatter/pointer.hpp42
-rw-r--r--libs/kstd/include/kstd/bits/format/formatter/string_view.hpp41
-rw-r--r--libs/kstd/include/kstd/bits/format/fwd.hpp23
-rw-r--r--libs/kstd/include/kstd/bits/format/parse_context.hpp (renamed from libs/kstd/include/kstd/bits/format_parse_context.hpp)27
-rw-r--r--libs/kstd/include/kstd/bits/format/specifiers.hpp (renamed from libs/kstd/include/kstd/bits/format_specifiers.hpp)10
-rw-r--r--libs/kstd/include/kstd/bits/format/string.hpp (renamed from libs/kstd/include/kstd/bits/format_string.hpp)28
-rw-r--r--libs/kstd/include/kstd/bits/format_args.hpp68
-rw-r--r--libs/kstd/include/kstd/bits/format_context.hpp74
-rw-r--r--libs/kstd/include/kstd/bits/formatter.hpp435
-rw-r--r--libs/kstd/include/kstd/format15
-rw-r--r--libs/kstd/include/kstd/os/print.hpp2
-rw-r--r--libs/kstd/include/kstd/print1
21 files changed, 772 insertions, 621 deletions
diff --git a/libs/kstd/include/kstd/bits/format/arg.hpp b/libs/kstd/include/kstd/bits/format/arg.hpp
new file mode 100644
index 0000000..92e6431
--- /dev/null
+++ b/libs/kstd/include/kstd/bits/format/arg.hpp
@@ -0,0 +1,26 @@
+#ifndef KSTD_BITS_FORMAT_ARG_HPP
+#define KSTD_BITS_FORMAT_ARG_HPP
+
+// IWYU pragma: private, include <kstd/format>
+
+#include "fwd.hpp"
+
+#include <cstddef>
+
+namespace kstd
+{
+
+ struct format_arg
+ {
+ using format_function_type = auto(void const * value, format_parse_context & parse_context,
+ format_context & context) -> void;
+ using get_size_function_type = auto(void const * value) -> std::size_t;
+
+ void const * value_pointer;
+ format_function_type * format_function;
+ get_size_function_type * get_size_function;
+ };
+
+} // namespace kstd
+
+#endif \ No newline at end of file
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..5cca3ff
--- /dev/null
+++ b/libs/kstd/include/kstd/bits/format/args.hpp
@@ -0,0 +1,75 @@
+#ifndef KSTD_BITS_FORMAT_ARGS_HPP
+#define KSTD_BITS_FORMAT_ARGS_HPP
+
+// IWYU pragma: private, include <kstd/format>
+
+#include "context.hpp"
+#include "error.hpp"
+#include "fwd.hpp"
+#include "parse_context.hpp"
+
+#include <array>
+#include <cstddef>
+#include <span>
+#include <type_traits>
+
+namespace kstd
+{
+
+ namespace bits::format
+ {
+
+ 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 ValueType>
+ auto get_size_trampoline(void const * value_pointer) -> std::size_t
+ {
+ if constexpr (is_width_v<ValueType>)
+ {
+ return static_cast<std::size_t>(*static_cast<ValueType const *>(value_pointer));
+ }
+ else
+ {
+ error("Dynamic width argument is not an integral value.");
+ return 0;
+ }
+ }
+
+ } // namespace bits::format
+
+ 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... Arguments>
+ [[nodiscard]] auto make_format_args(Arguments const &... args) noexcept -> format_arg_store<sizeof...(Arguments)>
+ {
+ using namespace bits::format;
+
+ 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>, get_size_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
new file mode 100644
index 0000000..478a48f
--- /dev/null
+++ b/libs/kstd/include/kstd/bits/format/context.hpp
@@ -0,0 +1,61 @@
+#ifndef KSTD_BITS_FORMAT_CONTEXT_HPP
+#define KSTD_BITS_FORMAT_CONTEXT_HPP
+
+// IWYU pragma: private, include <kstd/format>
+
+#include "arg.hpp"
+
+#include <kstd/os/error.hpp>
+
+#include <concepts>
+#include <cstddef>
+#include <span>
+#include <string_view>
+
+namespace kstd
+{
+
+ namespace bits::format
+ {
+ template<typename T>
+ constexpr auto inline is_width_v = std::integral<T> && //
+ !std::same_as<bool, T> && //
+ !std::same_as<char, T> && //
+ !std::same_as<wchar_t, T> && //
+ !std::same_as<char8_t, T> && //
+ !std::same_as<char16_t, T> && //
+ !std::same_as<char32_t, T>;
+ }
+
+ struct format_context
+ {
+ using writer_function = void(void *, std::string_view);
+ using format_args = std::span<format_arg const>;
+
+ writer_function * writer{};
+ void * user_data{};
+ format_args args{};
+
+ [[nodiscard]] auto arg(std::size_t const id) const -> format_arg const &
+ {
+ if (id >= args.size())
+ {
+ kstd::os::panic("[kstd:format] argument index out of range!");
+ }
+ return args[id];
+ }
+
+ constexpr auto push(std::string_view string) -> void
+ {
+ writer(user_data, string);
+ }
+
+ constexpr auto push(char character) -> void
+ {
+ writer(user_data, std::string_view(&character, 1));
+ }
+ };
+
+} // namespace kstd
+
+#endif \ No newline at end of file
diff --git a/libs/kstd/include/kstd/bits/format/error.hpp b/libs/kstd/include/kstd/bits/format/error.hpp
new file mode 100644
index 0000000..f0863eb
--- /dev/null
+++ b/libs/kstd/include/kstd/bits/format/error.hpp
@@ -0,0 +1,24 @@
+#ifndef KSTD_BITS_FORMAT_ERROR_HPP
+#define KSTD_BITS_FORMAT_ERROR_HPP
+
+#include "kstd/os/error.hpp"
+
+namespace kstd::bits::format
+{
+
+ constexpr auto 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.");
+ }
+ }
+
+} // namespace kstd::bits::format
+
+#endif \ No newline at end of file
diff --git a/libs/kstd/include/kstd/bits/format/formatter.hpp b/libs/kstd/include/kstd/bits/format/formatter.hpp
new file mode 100644
index 0000000..bff5f55
--- /dev/null
+++ b/libs/kstd/include/kstd/bits/format/formatter.hpp
@@ -0,0 +1,14 @@
+#ifndef KSTD_BITS_FORMATTER_HPP
+#define KSTD_BITS_FORMATTER_HPP
+
+// IWYU pragma: private, include <kstd/format>
+
+namespace kstd
+{
+
+ template<typename>
+ struct formatter;
+
+} // namespace kstd
+
+#endif \ No newline at end of file
diff --git a/libs/kstd/include/kstd/bits/format/formatter/bool.hpp b/libs/kstd/include/kstd/bits/format/formatter/bool.hpp
new file mode 100644
index 0000000..bb6cacf
--- /dev/null
+++ b/libs/kstd/include/kstd/bits/format/formatter/bool.hpp
@@ -0,0 +1,83 @@
+#ifndef KSTD_BITS_FORMAT_FORMATTER_BOOL_HPP
+#define KSTD_BITS_FORMAT_FORMATTER_BOOL_HPP
+
+#include "../context.hpp"
+#include "../error.hpp"
+#include "../formatter.hpp"
+#include "../parse_context.hpp"
+#include "../specifiers.hpp"
+
+#include <iterator>
+#include <string_view>
+
+namespace kstd
+{
+
+ template<>
+ struct formatter<bool>
+ {
+ bits::format::format_specifiers specifiers{};
+
+ constexpr auto parse(format_parse_context & context) -> format_parse_context::iterator
+ {
+ specifiers = bits::format::parse_format_specifiers(context);
+
+ auto it = context.begin();
+ auto const end = context.end();
+
+ if (it != end && *it != '}')
+ {
+ if (*it == 's')
+ {
+ specifiers.type = *it;
+ std::advance(it, 1);
+ }
+ else
+ {
+ bits::format::error("Invalid type specifier for bool.");
+ }
+ }
+
+ if (it != end && *it != '}')
+ {
+ bits::format::error("Missing terminating '}' in format string.");
+ }
+
+ return it;
+ }
+
+ auto format(bool value, format_context & context) const -> void
+ {
+ auto const text = value ? std::string_view{"true"} : std::string_view{"false"};
+ auto final_width = 0uz;
+
+ if (specifiers.width_mode == bits::format::width_mode::static_value)
+ {
+ final_width = specifiers.width_value;
+ }
+ else if (specifiers.width_mode == bits::format::width_mode::dynamic_argument_id)
+ {
+ auto const & arg = context.arg(specifiers.width_value);
+ final_width = arg.get_size_function(arg.value_pointer);
+ }
+
+ auto padding = bits::format::calculate_format_padding(final_width, text.size(), specifiers.align,
+ bits::format::alignment::left);
+
+ for (auto i = 0uz; i < padding.left; ++i)
+ {
+ context.push(specifiers.fill);
+ }
+
+ context.push(text);
+
+ for (auto i = 0uz; i < padding.right; ++i)
+ {
+ context.push(specifiers.fill);
+ }
+ }
+ };
+
+} // namespace kstd
+
+#endif \ No newline at end of file
diff --git a/libs/kstd/include/kstd/bits/format/formatter/cstring.hpp b/libs/kstd/include/kstd/bits/format/formatter/cstring.hpp
new file mode 100644
index 0000000..9afb974
--- /dev/null
+++ b/libs/kstd/include/kstd/bits/format/formatter/cstring.hpp
@@ -0,0 +1,29 @@
+#ifndef KSTD_BITS_FORMAT_FORMATTER_CSTRING_HPP
+#define KSTD_BITS_FORMAT_FORMATTER_CSTRING_HPP
+
+#include "../context.hpp"
+#include "../formatter.hpp"
+#include "string_view.hpp"
+
+#include <string_view>
+
+namespace kstd
+{
+
+ 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<>
+ struct formatter<char *> : formatter<char const *>
+ {
+ };
+
+} // namespace kstd
+
+#endif \ No newline at end of file
diff --git a/libs/kstd/include/kstd/bits/format/formatter/integral.hpp b/libs/kstd/include/kstd/bits/format/formatter/integral.hpp
new file mode 100644
index 0000000..b0caed1
--- /dev/null
+++ b/libs/kstd/include/kstd/bits/format/formatter/integral.hpp
@@ -0,0 +1,204 @@
+#ifndef KSTD_BITS_FORMAT_FORMATTER_INTEGRAL_HPP
+#define KSTD_BITS_FORMAT_FORMATTER_INTEGRAL_HPP
+
+#include "../context.hpp"
+#include "../error.hpp"
+#include "../formatter.hpp"
+#include "../parse_context.hpp"
+#include "../specifiers.hpp"
+
+#include <array>
+#include <concepts>
+#include <cstddef>
+#include <iterator>
+#include <string_view>
+#include <type_traits>
+#include <utility>
+
+namespace kstd
+{
+
+ template<std::integral T>
+ struct formatter<T>
+ {
+ bits::format::format_specifiers specifiers{};
+
+ constexpr auto static maximum_digits = 80;
+
+ enum struct base
+ {
+ bin = 2,
+ oct = 8,
+ dec = 10,
+ hex = 16,
+ };
+
+ constexpr auto parse(format_parse_context & context) -> format_parse_context::iterator
+ {
+ specifiers = bits::format::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
+ {
+ bits::format::error("Invalid type specifier for integral type.");
+ }
+ }
+
+ if (it != end && *it != '}')
+ {
+ bits::format::error("Missing terminating '}' in format string.");
+ }
+
+ return it;
+ }
+
+ auto format(T value, format_context & context) const -> void
+ {
+ auto final_width = 0uz;
+ if (specifiers.width_mode == bits::format::width_mode::static_value)
+ {
+ final_width = specifiers.width_value;
+ }
+ else if (specifiers.width_mode == bits::format::width_mode::dynamic_argument_id)
+ {
+ auto const & arg = context.arg(specifiers.width_value);
+ final_width = arg.get_size_function(arg.value_pointer);
+ }
+
+ using unsigned_T = std::make_unsigned_t<T>;
+ auto absolute_value = static_cast<unsigned_T>(value);
+ auto is_negative = false;
+
+ if constexpr (std::is_signed_v<T>)
+ {
+ if (value < 0)
+ {
+ is_negative = true;
+ absolute_value = 0 - static_cast<unsigned_T>(value);
+ }
+ }
+
+ auto const base = [type = specifiers.type] -> auto {
+ switch (type)
+ {
+ case 'x':
+ case 'X':
+ case 'p':
+ return base::hex;
+ case 'b':
+ case 'B':
+ return base::bin;
+ case 'o':
+ return base::oct;
+ default:
+ return base::dec;
+ }
+ }();
+
+ auto buffer = std::array<char, maximum_digits>{};
+ auto digits = (specifiers.type == 'X') ? "0123456789ABCDEF" : "0123456789abcdef";
+ auto current = buffer.rbegin();
+
+ if (absolute_value == 0)
+ {
+ *current = '0';
+ std::advance(current, 1);
+ }
+ else
+ {
+ while (absolute_value != 0)
+ {
+ *current = digits[absolute_value % std::to_underlying(base)];
+ std::advance(current, 1);
+ absolute_value /= std::to_underlying(base);
+ }
+ }
+
+ 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 (specifiers.alternative_form)
+ {
+ switch (base)
+ {
+ case base::bin:
+ prefix[1] = specifiers.type == 'B' ? 'B' : 'b';
+ prefix_length = 2;
+ break;
+ case base::oct:
+ prefix_length = 1;
+ break;
+ case base::hex:
+ prefix[1] = specifiers.type == 'X' ? 'X' : 'x';
+ prefix_length = 2;
+ break;
+ default:
+ break;
+ }
+ }
+
+ auto sign_character = '\0';
+ if (is_negative)
+ {
+ sign_character = '-';
+ }
+ else if (specifiers.sign == bits::format::sign_mode::plus)
+ {
+ sign_character = '+';
+ }
+ else if (specifiers.sign == bits::format::sign_mode::space)
+ {
+ sign_character = ' ';
+ }
+
+ auto const total_length = content_length + prefix_length + (sign_character != '\0');
+ auto const padding = bits::format::calculate_format_padding(final_width, total_length, specifiers.align,
+ bits::format::alignment::right);
+ auto const effective_zero_pad = specifiers.zero_pad && (specifiers.align == bits::format::alignment::none);
+
+ if (!effective_zero_pad)
+ {
+ for (auto i = 0uz; i < padding.left; ++i)
+ {
+ context.push(specifiers.fill);
+ }
+ }
+
+ if (sign_character != '\0')
+ {
+ context.push(sign_character);
+ }
+ if (prefix_length > 0)
+ {
+ context.push(std::string_view{prefix.data(), prefix_length});
+ }
+
+ if (effective_zero_pad)
+ {
+ for (auto i = 0uz; i < padding.left; ++i)
+ {
+ context.push('0');
+ }
+ }
+
+ context.push(std::string_view{current.base(), content_length});
+
+ for (auto i = 0uz; i < padding.right; ++i)
+ {
+ context.push(specifiers.fill);
+ }
+ }
+ };
+
+} // namespace kstd
+
+#endif \ No newline at end of file
diff --git a/libs/kstd/include/kstd/bits/format/formatter/ordering.hpp b/libs/kstd/include/kstd/bits/format/formatter/ordering.hpp
new file mode 100644
index 0000000..78e7f7b
--- /dev/null
+++ b/libs/kstd/include/kstd/bits/format/formatter/ordering.hpp
@@ -0,0 +1,111 @@
+#ifndef KSTD_BITS_FORMAT_FORMATTER_ORDERING_HPP
+#define KSTD_BITS_FORMAT_FORMATTER_ORDERING_HPP
+
+#include "../context.hpp"
+#include "../formatter.hpp"
+#include "../parse_context.hpp"
+#include "../specifiers.hpp"
+
+#include <compare>
+
+namespace kstd
+{
+
+ template<>
+ struct formatter<std::strong_ordering>
+ {
+ bits::format::format_specifiers specifiers{};
+
+ constexpr auto parse(format_parse_context & context) -> format_parse_context::iterator
+ {
+ specifiers = bits::format::parse_format_specifiers(context);
+ return context.begin();
+ }
+
+ auto format(std::strong_ordering value, format_context & context) const -> void
+ {
+ if (value == std::strong_ordering::equal)
+ {
+ return context.push(specifiers.alternative_form ? "==" : "equal");
+ }
+ else if (value == std::strong_ordering::equivalent)
+ {
+ return context.push(specifiers.alternative_form ? "==" : "equivalent");
+ }
+ else if (value == std::strong_ordering::greater)
+ {
+ return context.push(specifiers.alternative_form ? ">" : "greater");
+ }
+ else if (value == std::strong_ordering::less)
+ {
+ return context.push(specifiers.alternative_form ? "<" : "less");
+ }
+ kstd::os::panic("[kstd:format] Invalid strong ordering value!");
+ }
+ };
+
+ template<>
+ struct formatter<std::weak_ordering>
+ {
+ bits::format::format_specifiers specifiers{};
+
+ constexpr auto parse(format_parse_context & context) -> format_parse_context::iterator
+ {
+ specifiers = bits::format::parse_format_specifiers(context);
+ return context.begin();
+ }
+
+ auto format(std::weak_ordering value, format_context & context) const -> void
+ {
+ if (value == std::weak_ordering::equivalent)
+ {
+ return context.push(specifiers.alternative_form ? "==" : "equivalent");
+ }
+ else if (value == std::weak_ordering::greater)
+ {
+ return context.push(specifiers.alternative_form ? ">" : "greater");
+ }
+ else if (value == std::weak_ordering::less)
+ {
+ return context.push(specifiers.alternative_form ? "<" : "less");
+ }
+ kstd::os::panic("[kstd:format] Invalid weak ordering value!");
+ }
+ };
+
+ template<>
+ struct formatter<std::partial_ordering>
+ {
+ bits::format::format_specifiers specifiers{};
+
+ constexpr auto parse(format_parse_context & context) -> format_parse_context::iterator
+ {
+ specifiers = bits::format::parse_format_specifiers(context);
+ return context.begin();
+ }
+
+ auto format(std::partial_ordering value, format_context & context) const -> void
+ {
+ if (value == std::partial_ordering::equivalent)
+ {
+ return context.push(specifiers.alternative_form ? "==" : "equivalent");
+ }
+ else if (value == std::partial_ordering::greater)
+ {
+ return context.push(specifiers.alternative_form ? ">" : "greater");
+ }
+ else if (value == std::partial_ordering::less)
+ {
+ return context.push(specifiers.alternative_form ? "<" : "less");
+ }
+ else if (value == std::partial_ordering::unordered)
+ {
+ return context.push(specifiers.alternative_form ? "<=>" : "unordered");
+ }
+ kstd::os::panic("[kstd:format] Invalid partial ordering value!");
+ }
+ };
+
+} // namespace kstd
+
+#endif \ No newline at end of file
diff --git a/libs/kstd/include/kstd/bits/format/formatter/pointer.hpp b/libs/kstd/include/kstd/bits/format/formatter/pointer.hpp
new file mode 100644
index 0000000..fe75a2f
--- /dev/null
+++ b/libs/kstd/include/kstd/bits/format/formatter/pointer.hpp
@@ -0,0 +1,42 @@
+#ifndef KSTD_BITS_FORMAT_FORMATTER_POINTER_HPP
+#define KSTD_BITS_FORMAT_FORMATTER_POINTER_HPP
+
+#include "../context.hpp"
+#include "../formatter.hpp"
+#include "../parse_context.hpp"
+#include "../specifiers.hpp"
+#include "integral.hpp"
+
+#include <bit>
+#include <cstdint>
+
+namespace kstd
+{
+
+ template<typename T>
+ struct formatter<T const *> : formatter<std::uintptr_t>
+ {
+ constexpr auto parse(format_parse_context & context) -> format_parse_context::iterator
+ {
+ auto result = formatter<std::uintptr_t>::parse(context);
+ if (!this->specifiers.type)
+ {
+ this->specifiers.type = 'p';
+ this->specifiers.alternative_form = true;
+ }
+ return result;
+ }
+
+ auto format(T const * pointer, format_context & context) const -> void
+ {
+ formatter<std::uintptr_t>::format(std::bit_cast<std::uintptr_t>(pointer), context);
+ }
+ };
+
+ template<typename T>
+ struct formatter<T *> : formatter<T const *>
+ {
+ };
+
+} // namespace kstd
+#endif \ No newline at end of file
diff --git a/libs/kstd/include/kstd/bits/format/formatter/string_view.hpp b/libs/kstd/include/kstd/bits/format/formatter/string_view.hpp
new file mode 100644
index 0000000..f5b698e
--- /dev/null
+++ b/libs/kstd/include/kstd/bits/format/formatter/string_view.hpp
@@ -0,0 +1,41 @@
+#ifndef KSTD_BITS_FORMAT_FORMATTER_STRING_VIEW_HPP
+#define KSTD_BITS_FORMAT_FORMATTER_STRING_VIEW_HPP
+
+#include "../context.hpp"
+#include "../error.hpp"
+#include "../formatter.hpp"
+#include "../parse_context.hpp"
+
+#include <string_view>
+
+namespace kstd
+{
+
+ 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 != '}')
+ {
+ bits::format::error("Invalid specifier for string_view.");
+ }
+ return it;
+ }
+
+ auto format(std::string_view const & string, format_context & context) const -> void
+ {
+ context.push(string);
+ }
+ };
+
+} // namespace kstd
+
+#endif \ No newline at end of file
diff --git a/libs/kstd/include/kstd/bits/format/fwd.hpp b/libs/kstd/include/kstd/bits/format/fwd.hpp
new file mode 100644
index 0000000..6caedae
--- /dev/null
+++ b/libs/kstd/include/kstd/bits/format/fwd.hpp
@@ -0,0 +1,23 @@
+#ifndef KSTD_BITS_FORMAT_FWD_HPP
+#define KSTD_BITS_FORMAT_FWD_HPP
+
+// IWYU pragma: private
+
+#include <cstddef>
+
+namespace kstd
+{
+
+ struct format_parse_context;
+ struct format_context;
+ struct format_arg;
+
+ template<typename>
+ struct formatter;
+
+ template<std::size_t>
+ struct format_arg_store;
+
+} // namespace kstd
+
+#endif \ No newline at end of file
diff --git a/libs/kstd/include/kstd/bits/format_parse_context.hpp b/libs/kstd/include/kstd/bits/format/parse_context.hpp
index 76c3f03..063263b 100644
--- a/libs/kstd/include/kstd/bits/format_parse_context.hpp
+++ b/libs/kstd/include/kstd/bits/format/parse_context.hpp
@@ -3,7 +3,7 @@
// IWYU pragma: private, include <kstd/format>
-#include <kstd/os/error.hpp>
+#include "error.hpp"
#include <cstddef>
#include <string_view>
@@ -11,19 +11,6 @@
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;
@@ -55,14 +42,14 @@ namespace kstd
{
if (m_mode == index_mode::manual)
{
- report_format_error("Cannot mix automatic and manual indexing.");
+ bits::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.");
+ bits::format::error("Argument index out of bounds.");
}
return m_next_argument_id++;
}
@@ -71,14 +58,14 @@ namespace kstd
{
if (m_mode == index_mode::automatic)
{
- report_format_error("Cannot mix automatic and manual indexing.");
+ bits::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.");
+ bits::format::error("Argument index out of bounds.");
}
}
@@ -87,7 +74,7 @@ namespace kstd
check_arg_id(id);
if (m_is_integral && !m_is_integral[id])
{
- report_format_error("Dynamic width argument must be an integral object.");
+ bits::format::error("Dynamic width argument must be an integral object.");
}
}
@@ -96,7 +83,7 @@ namespace kstd
auto const id = next_arg_id();
if (m_is_integral && !m_is_integral[id])
{
- report_format_error("Dynamic width argument must be an integral object.");
+ bits::format::error("Dynamic width argument must be an integral object.");
}
return id;
}
diff --git a/libs/kstd/include/kstd/bits/format_specifiers.hpp b/libs/kstd/include/kstd/bits/format/specifiers.hpp
index 00cca40..85581e6 100644
--- a/libs/kstd/include/kstd/bits/format_specifiers.hpp
+++ b/libs/kstd/include/kstd/bits/format/specifiers.hpp
@@ -3,14 +3,14 @@
// IWYU pragma: private
-#include "kstd/bits/format_context.hpp"
-#include "kstd/bits/format_parse_context.hpp"
+#include "error.hpp"
+#include "parse_context.hpp"
#include <cstddef>
#include <cstdint>
#include <iterator>
-namespace kstd::bits
+namespace kstd::bits::format
{
enum struct alignment : std::uint8_t
{
@@ -153,7 +153,7 @@ namespace kstd::bits
if (it == end || *it != '}')
{
- report_format_error("Expected '}' for dynamic width.");
+ error("Expected '}' for dynamic width.");
}
std::advance(it, 1);
specifiers.width_value = argument_id;
@@ -200,6 +200,6 @@ namespace kstd::bits
}
}
-} // namespace kstd::bits
+} // namespace kstd::bits::format
#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 f16f1ee..2e7d60a 100644
--- a/libs/kstd/include/kstd/bits/format_string.hpp
+++ b/libs/kstd/include/kstd/bits/format/string.hpp
@@