19#ifndef MTCORE_FORMATS_HPP
20#define MTCORE_FORMATS_HPP
72 if (formatStr.
empty()) {
76 if (!semiColon.has_value()) {
85 ensure(res.is_success(),
"BAD FORMAT STRING!");
86 return res.value() == 1;
89 auto alignmentSearchSpace = formatStr.
sub(0, semiColon.value());
91 if (leftPadLoc.has_value() && leftPadLoc.value() > 0) {
92 auto n = alignmentSearchSpace.sub(leftPadLoc.value() + 1);
94 auto pad = formatStr.
sub(0, leftPadLoc.value());
95 if (has_single_char(pad)) {
101 .endPos = semiColon.value(),
108 if (rightPadLoc.has_value() && rightPadLoc.value() > 0) {
109 auto n = alignmentSearchSpace.sub(rightPadLoc.value() + 1);
111 auto pad = formatStr.
sub(0, rightPadLoc.value());
112 if (has_single_char(pad)) {
118 .endPos = semiColon.value(),
125 if (centerPadLoc.has_value() && centerPadLoc.value() > 0) {
126 auto n = alignmentSearchSpace.sub(centerPadLoc.value() + 1);
128 auto pad = formatStr.
sub(0, centerPadLoc.value());
129 if (has_single_char(pad)) {
135 .endPos = semiColon.value(),
154 template<WriterImpl WI>
175 template<WriterImpl WI>
194 template<std::
integral T>
197 static_cast<i64>(1ll),
198 static_cast<i64>(10ll),
199 static_cast<i64>(100ll),
200 static_cast<i64>(1000ll),
201 static_cast<i64>(10000ll),
202 static_cast<i64>(100000ll),
203 static_cast<i64>(1000000ll),
204 static_cast<i64>(10000000ll),
205 static_cast<i64>(100000000ll),
206 static_cast<i64>(1000000000ll),
207 static_cast<i64>(10000000000ll),
208 static_cast<i64>(100000000000ll),
209 static_cast<i64>(1000000000000ll),
210 static_cast<i64>(10000000000000ll),
211 static_cast<i64>(100000000000000ll),
212 static_cast<i64>(1000000000000000ll),
213 static_cast<i64>(10000000000000000ll),
214 static_cast<i64>(100000000000000000ll),
215 static_cast<i64>(1000000000000000000ll),
218 template<WriterImpl WI>
222 if (padOpts.has_value()) {
229 if constexpr (std::is_signed_v<T>) {
234 auto tmpPtr =
reinterpret_cast<std::make_unsigned_t<T> *
>(&tmp);
242 static constexpr auto chars =
slice_from(
"0123456789ABCDEF");
244 auto hexChars = std::array<char, 16>{
245 static_cast<char>(chars[(uval >> 60) & 0xfull]),
static_cast<char>(chars[(uval >> 56) & 0xfull]),
246 static_cast<char>(chars[(uval >> 52) & 0xfull]),
static_cast<char>(chars[(uval >> 48) & 0xfull]),
247 static_cast<char>(chars[(uval >> 44) & 0xfull]),
static_cast<char>(chars[(uval >> 40) & 0xfull]),
248 static_cast<char>(chars[(uval >> 36) & 0xfull]),
static_cast<char>(chars[(uval >> 32) & 0xfull]),
249 static_cast<char>(chars[(uval >> 28) & 0xfull]),
static_cast<char>(chars[(uval >> 24) & 0xfull]),
250 static_cast<char>(chars[(uval >> 20) & 0xfull]),
static_cast<char>(chars[(uval >> 16) & 0xfull]),
251 static_cast<char>(chars[(uval >> 12) & 0xfull]),
static_cast<char>(chars[(uval >> 8) & 0xfull]),
252 static_cast<char>(chars[(uval >> 4) & 0xfull]),
static_cast<char>(chars[(uval >> 0) & 0xfull]),
258 if (start.has_value()) {
259 auto out = hex.sub(start.value());
260 written += out.size();
261 auto res = writer.write_all(out);
262 return res.with_success_val(written);
266 return writer.write(
'0').with_success_val(written);
271 static constexpr auto chars =
slice_from(
"0123456789abcdef");
273 auto hexChars = std::array<char, 16>{
274 static_cast<char>(chars[(uval >> 60) & 0xfull]),
static_cast<char>(chars[(uval >> 56) & 0xfull]),
275 static_cast<char>(chars[(uval >> 52) & 0xfull]),
static_cast<char>(chars[(uval >> 48) & 0xfull]),
276 static_cast<char>(chars[(uval >> 44) & 0xfull]),
static_cast<char>(chars[(uval >> 40) & 0xfull]),
277 static_cast<char>(chars[(uval >> 36) & 0xfull]),
static_cast<char>(chars[(uval >> 32) & 0xfull]),
278 static_cast<char>(chars[(uval >> 28) & 0xfull]),
static_cast<char>(chars[(uval >> 24) & 0xfull]),
279 static_cast<char>(chars[(uval >> 20) & 0xfull]),
static_cast<char>(chars[(uval >> 16) & 0xfull]),
280 static_cast<char>(chars[(uval >> 12) & 0xfull]),
static_cast<char>(chars[(uval >> 8) & 0xfull]),
281 static_cast<char>(chars[(uval >> 4) & 0xfull]),
static_cast<char>(chars[(uval >> 0) & 0xfull]),
287 if (start.has_value()) {
288 auto out = hex.sub(start.value());
289 written += out.size();
290 auto res = writer.write_all(out);
291 return res.with_success_val(written);
295 return writer.write(
'0').with_success_val(written);
300 static constexpr auto chars =
slice_from(
"01234567");
302 auto hexChars = std::array<char, 22>{
303 static_cast<char>(chars[(uval >> 63) & 0x1ull]),
static_cast<char>(chars[(uval >> 60) & 0x7ull]),
304 static_cast<char>(chars[(uval >> 57) & 0x7ull]),
static_cast<char>(chars[(uval >> 54) & 0x7ull]),
305 static_cast<char>(chars[(uval >> 51) & 0x7ull]),
static_cast<char>(chars[(uval >> 48) & 0x7ull]),
306 static_cast<char>(chars[(uval >> 45) & 0x7ull]),
static_cast<char>(chars[(uval >> 42) & 0x7ull]),
307 static_cast<char>(chars[(uval >> 39) & 0x7ull]),
static_cast<char>(chars[(uval >> 36) & 0x7ull]),
308 static_cast<char>(chars[(uval >> 33) & 0x7ull]),
static_cast<char>(chars[(uval >> 30) & 0x7ull]),
309 static_cast<char>(chars[(uval >> 27) & 0x7ull]),
static_cast<char>(chars[(uval >> 24) & 0x7ull]),
310 static_cast<char>(chars[(uval >> 21) & 0x7ull]),
static_cast<char>(chars[(uval >> 18) & 0x7ull]),
311 static_cast<char>(chars[(uval >> 15) & 0x7ull]),
static_cast<char>(chars[(uval >> 12) & 0x7ull]),
312 static_cast<char>(chars[(uval >> 9) & 0x7ull]),
static_cast<char>(chars[(uval >> 6) & 0x7ull]),
313 static_cast<char>(chars[(uval >> 3) & 0x7ull]),
static_cast<char>(chars[(uval >> 0) & 0x7ull]),
319 if (start.has_value()) {
320 auto out = hex.sub(start.value());
321 written += out.size();
322 auto res = writer.write_all(out);
323 return res.with_success_val(written);
327 return writer.write(
'0').with_success_val(written);
339 if (firstBit.has_value()) {
340 ensure(lastBit.has_value());
341 for (
size_t j = lastBit.value() + 1; j > 0; --j) {
344 auto chRes = writer.write(bits.
at(i) ?
'1' :
'0');
345 if (chRes.is_error()) {
346 return chRes.error();
354 return writer.write(
'0').with_success_val(written);
358 if constexpr (std::is_signed_v<T>) {
360 if (
auto signRes = writer.write(
'-'); signRes.is_error()) {
361 return signRes.error();
368 if (
auto zRes = writer.write(
'0'); zRes.is_error()) {
376 for (
auto numDigits =
num_digits(val); numDigits > 0; --numDigits) {
378 ensure(digit >= 0 && digit < 10);
379 if (
auto chRes = writer.write(digit +
'0'); chRes.is_error()) {
380 return chRes.error();
387 for (
auto numDigits =
num_digits(val); numDigits > 0; --numDigits) {
388 auto digit = -(v /
digitTable[numDigits - 1]);
389 ensure(digit >= 0 && digit < 10);
390 if (
auto chRes = writer.write(digit +
'0'); chRes.is_error()) {
391 return chRes.error();
403 if constexpr (std::is_signed_v<T>) {
406 for (
size_t i = 0; i <
digitTable.size(); ++i) {
415 for (
size_t i = 0; i <
digitTable.size(); ++i) {
442 template<std::
floating_po
int T>
443 struct Formatter<T> {
444 template<WriterImpl WI>
447 if (padOpts.has_value()) {
484 struct Formatter<T> {
485 using ElemType = std::remove_const_t<typename decltype(std::declval<T>().iter())::IterElem>;
487 template<WriterImpl WI>
489 size_t maxElems = std::numeric_limits<size_t>::max();
492 if (endMaxElemPos.has_value()) {
493 auto rng = opts.
formatStr.
sub(0, endMaxElemPos.value());
501 if (padOpts.has_value()) {
507 if constexpr (std::is_same_v<ElemType, char>) {
514 if (sepIndex.has_value()) {
519 auto iter = iterable.iter();
524 while (elem++ < maxElems &&
iter.next().move_if_present(cur)) {
530 if (sepRes.is_error()) {
531 auto errCode = sepRes.error().code;
532 ensure(!std::holds_alternative<ascii::UnescapeError>(errCode),
"BAD FORMAT STRING");
535 written += sepRes.value();
541 if (fmtSegEnd.has_value()) {
545 if (fmtSegEnd.has_value()) {
550 if (elemRes.is_error()) {
551 return elemRes.error();
553 written += elemRes.value();
589 template<StdIterable T>
590 struct Formatter<T> {
591 using ElemType = std::remove_reference_t<std::remove_const_t<decltype(*std::begin(std::declval<T &>()))>>;
593 template<WriterImpl WI>
595 size_t maxElems = std::numeric_limits<size_t>::max();
599 if (endMaxElemPos.has_value()) {
600 auto rng = opts.
formatStr.
sub(0, endMaxElemPos.value());
609 if (padOpts.has_value()) {
615 if constexpr (std::is_same_v<ElemType, char>) {
622 if (sepIndex.has_value()) {
630 for (
const auto &cur: iterable) {
631 if (elem++ >= maxElems) {
639 if (sepRes.is_error()) {
640 auto errCode = sepRes.error().code;
641 ensure(!std::holds_alternative<ascii::UnescapeError>(errCode),
"BAD FORMAT STRING");
644 written += sepRes.value();
650 if (fmtSegEnd.has_value()) {
654 if (fmtSegEnd.has_value()) {
659 if (elemRes.is_error()) {
660 return elemRes.error();
662 written += elemRes.value();
677 template<WriterImpl WI>
692 template<WriterImpl WI>
699 template<WriterImpl WI>
703 if (padOpts.has_value()) {
708 auto bits = std::array<int, 8>{
709 (ch >> 7) & 0x1, (ch >> 6) & 0x1, (ch >> 5) & 0x1, (ch >> 4) & 0x1,
710 (ch >> 3) & 0x1, (ch >> 2) & 0x1, (ch >> 1) & 0x1, (ch >> 0) & 0x1,
716 if (!start.has_value()) {
725 constexpr auto chars =
"0123456789ABCDEF";
727 auto c = chars[(ch >> 4) & 0xf];
730 auto ch1 = writer.write(c);
731 if (ch1.is_error()) {
735 auto ch2 = writer.write(chars[ch & 0xf]);
736 if (ch2.is_error()) {
739 return static_cast<size_t>(c !=
'0' ? 2 : 1);
742 constexpr auto chars =
"0123456789abcdef";
743 auto c = chars[(ch >> 4) & 0xf];
746 auto ch1 = writer.write(c);
747 if (ch1.is_error()) {
751 auto ch2 = writer.write(chars[ch & 0xf]);
752 if (ch2.is_error()) {
755 return static_cast<size_t>(c !=
'0' ? 2 : 1);
758 constexpr auto chars =
"01234567";
759 auto octalChars = std::array<char, 22>{
760 static_cast<char>(chars[(ch >> 6) & 0x5ull]),
761 static_cast<char>(chars[(ch >> 3) & 0x7ull]),
762 static_cast<char>(chars[(ch >> 0) & 0x7ull]),
768 if (start.has_value()) {
769 auto out = octal.sub(start.value());
770 written += out.size();
771 auto res = writer.write_all(out);
772 return res.with_success_val(written);
776 return writer.write(
'0').with_success_val(written);
780 auto v =
static_cast<u8>(ch);
784 return writer.write(ch).with_success_val(
static_cast<size_t>(1));
789 template<WriterImpl WI>
797 ensure(sizeRes.is_success());
798 size_t len = sizeRes.value();
801 if (len >= padOpts.
padLen) {
807 auto padRes = writer.write_n_times(padOpts.
pad, padOpts.
padLen - len);
808 if (padRes.is_error()) {
809 return padRes.error();
815 if (contentRes.is_error()) {
816 return contentRes.error();
818 return writer.write_n_times(padOpts.
pad, padOpts.
padLen - len).with_success_val(padOpts.
padLen);
821 auto leftPad = (padOpts.
padLen - len) / 2;
822 auto rightPad = leftPad;
825 if (leftPad + rightPad + len != padOpts.
padLen) {
828 auto padLeftRes = writer.write_n_times(padOpts.
pad, leftPad);
829 if (padLeftRes.is_error()) {
830 return padLeftRes.error();
833 if (contentRes.is_error()) {
834 return contentRes.error();
836 return writer.write_n_times(padOpts.
pad, rightPad).with_success_val(padOpts.
padLen);
856 template<WriterImpl WI>
859 return writer.write_all(
slice_from(
"nullopt"));
862 return print(writer,
"Optional\\{{}\\}", opt.
value());
865 return writer.write_all(
slice_from(
"Optional{?}"));
879 template<
typename V,
typename E>
881 template<WriterImpl WI>
884 return writer.write_all(
slice_from(
"Result{<MOVED>}"));
889 return print(writer,
"Result\\{<ERROR> {}\\}", res.
error());
891 else if constexpr (std::is_convertible_v<E, int>) {
892 const auto err =
static_cast<int>(res.
error());
893 auto typeId =
typeid(E).name();
894 return print(writer,
"Result\\{<ERROR> {} {}\\}",
slice_from(typeId), err);
897 return writer.write_all(
slice_from(
"Result\\{<ERROR>\\}"));
901 if constexpr (std::is_same_v<void, V>) {
902 return writer.write_all(
slice_from(
"Result{<SUCCESS>}"));
905 return print(writer,
"Result\\{<SUCCESS> {}\\}", res.
value());
908 return print(writer,
"Result\\{<SUCCESS> ?\\}");
927template<mtcore::Po
inter T>
929 template<WriterImpl WI>
931 if (ptr ==
nullptr) {
932 return writer.write_all(
slice_from(
"nullptr"));
937 return print(writer,
"*{}", *ptr);
944 auto addr =
reinterpret_cast<intptr_t
>(ptr);
946 return print(writer,
"{x}", addr);
949 return print(writer,
"0x{x}", addr);
951 return print(writer,
"Addr 0x{x}", addr);
955template<mtcore::InlineFormattable T>
957 template<WriterImpl WI>
959 return f.format(writer, opts);
964 template<Formattable WI>
Result< Out, AsciiNumericParseError > base10_to_int(const Slice< const char > &parse)
Tries to parse an ASCII numeric string into an integer Will return an error if the parsed string does...
bool is_pos_int_str(Slice< const char > str)
Checks if a string is composed of only positive integer characters Will return false if string is emp...
Result< size_t, std::variant< typename Writer::ErrType, UnescapeError > > write_unescaped(Writer &writer, Slice< const char > str, char escapeChar='\\')
Writes unescaped ASCII characters to a stream Note: This does NOT support writing Unicode escape code...
Result< char, UnescapeError > unescape_char(Slice< const char > str, char escapeChar='\\')
Unescapes the first (potentially) escaped character in a character sequence If the sequence is empty,...
constexpr auto nullopt
Placeholder value for an empty Optional.
mtcore::Optional< size_t > first_index_not_proceeded_by(const std::remove_const_t< T > &prefix, const std::remove_const_t< T > &needle, const Slice< T > &haystack)
Gets the first index that a needle appears in the haystack, or nullopt if the needle does not appear ...
constexpr Slice< const char32_t > slice_from(char32_t *cstr)
Creates a slice from a utf32 string in the form of a c string.
mtcore::Optional< size_t > first_index(const Slice< T > &needle, const Slice< T > &haystack)
Gets the first index that a needle appears in the haystack, or nullopt if the needle does not appear ...
mtcore::Optional< size_t > first_index_not(const std::remove_const_t< T > &needle, const Slice< T > &haystack)
Gets the first index that a needle appears in the haystack, or nullopt if the needle does not appear ...
#define ensure(check,...)
Ensures that a check holds true, aborts the program if not true Will print error if the condition is ...
#define unreachable(...)
Marks code as unreachable.
#define mtdefer
Defer statement that will mtdefer execution until the scope is left, at which point the code will run...
Success< void > success()
Creates a successful void Result object.
Error< Underlying > error(Underlying err)
Creates an error.
constexpr bool is_formattable
Checks if a type has a zero-parameter deinit.
uint64_t u64
Alias for 64-bit unsigned ints.
int64_t i64
Alias for 64-bit ints.
uint8_t u8
Alias for 8-bit unsigned ints.
double f64
Alias for 64-bit floats.
io::Writer< impl::VoidWriterImpl< T > > void_writer()
Creates a writer which discards what's written (think of it as writer to /dev/null) Useful for doing ...
Result< size_t, typename Writer< WI >::ErrType > format_float(Writer< WI > &writer, f64 f, Slice< const char > opts={nullptr, 0})
Generic iterator defaults built on common contracts Does not guarantee performance of iterators Actua...
bool str_equal(const L &left, const R &right)
Compares two string strings for ordering.
Represents a bitset with dynamically allocated memory (using an mtcore allocator) Allows operating on...
Optional< size_t > first_set() const
Returns the position of the first set bit (if one is set) Otherwise, returns a null Optional.
Optional< size_t > last_set() const
Returns the position of the last set bit (if one is set) Otherwise, returns a null Optional.
bool at(const size_t pos) const
Reads a bit at a specific position.
Represents a value that may or may not exist (an "Optional" value) Similar concept to std::optional,...
bool has_value() const noexcept
Represents a Result that may have an error (error code) or a success value A type of "void" means the...
T value() const noexcept
Checks if is a successful Result (aka.
bool is_success() const noexcept
Checks if is a successful Result (aka.
bool is_error() const noexcept
Checks if is an error Result.
ErrVal error() const noexcept
Returns the associated error Fails if there is no error;.
A Slice which is just a pointer + length Accessing elements through the array operator will do bounds...
constexpr Slice sub(size_t start) const noexcept
Gets a sub Slice from start.
constexpr bool empty() const noexcept
Checks if a Slice is empty.
Formats data with padding into a writer's output stream Uses Formatter<T> under the hood Usually will...
static Result< size_t, typename Writer< WI >::ErrType > write_padded(Writer< WI > &writer, const PaddingOptions &padOpts, const FormatOptions &opts, const T &elem)
Represents parsed padding options for formatting output.
ContentAlignment alignment
A writer that writes data to some sort of stream or buffer Note: the data elements written should be ...
typename Impl::ErrType ErrType