Iros
 
Loading...
Searching...
No Matches
base.h
Go to the documentation of this file.
1#pragma once
2
9#include "di/function/value.h"
10#include "di/math/abs.h"
13#include "di/parser/prelude.h"
14
15namespace di::format {
16namespace detail {
17 struct FillAndAlign {
18 enum class Align { Left, Center, Right };
19
20 char32_t fill;
22
23 private:
25 using namespace integral_set_literals;
26 return (parser::match_one(~('{'_m || '}'_m)) >> parser::match_one('<'_m || '^'_m || '>'_m)) %
27 [](concepts::TupleLike auto result) {
28 auto [fill, align_char] = result;
29 auto align = [](c32 align_char) {
30 switch (align_char) {
31 case U'<':
32 return Align::Left;
33 case U'^':
34 return Align::Center;
35 case U'>':
36 return Align::Right;
37 default:
39 }
40 }(align_char);
41
42 return FillAndAlign { fill, align };
43 };
44 }
45 };
46
47 enum class Sign { Plus, Minus, Space };
48
50 using namespace integral_set_literals;
51 return (-parser::match_one('+'_m || '-'_m || ' '_m)) % [](Optional<char32_t> ch) {
52 switch (ch.value_or(U'-')) {
53 case U'+':
54 return Sign::Plus;
55 case U'-':
56 return Sign::Minus;
57 case U' ':
58 return Sign::Space;
59 default:
61 }
62 };
63 }
64
65 enum class HashTag { Yes, No };
66
68 using namespace integral_set_literals;
69 return (-parser::match_one('#'_m)) % [](Optional<char32_t> value) {
70 return value.has_value() ? HashTag::Yes : HashTag::No;
71 };
72 }
73
74 enum class Zero { Yes, No };
75
77 using namespace integral_set_literals;
78 return (-parser::match_one('0'_m)) % [](Optional<char32_t> value) {
79 return value.has_value() ? Zero::Yes : Zero::No;
80 };
81 }
82
83 struct Width {
84 size_t value;
85
86 private:
88 return parser::integer<size_t>(10) % [](size_t value) {
89 return Width { value };
90 };
91 }
92 };
93
94 struct Precision {
95 size_t value;
96
97 private:
99 return (~parser::match_one('.'_m) >> parser::integer<size_t>(10)) % [](size_t value) {
100 return Precision { value };
101 };
102 }
103 };
104
105 enum class StringType { String, Debug };
106
108 using namespace integral_set_literals;
109 return (parser::match_one('s'_m || '?'_m)) % [](char32_t ch) {
110 switch (ch) {
111 case U's':
112 return StringType::String;
113 case U'?':
114 return StringType::Debug;
115 default:
117 }
118 };
119 }
120
122
124 using namespace integral_set_literals;
125 return (-parser::match_one('b'_m || 'B'_m || 'c'_m || 'd'_m || 'o'_m || 'x'_m || 'X'_m)) %
126 [](Optional<char32_t> ch) {
127 switch (ch.value_or(U'd')) {
128 case U'b':
130 case U'B':
132 case U'c':
134 case U'd':
136 case U'o':
137 return IntegerType::Octal;
138 case U'x':
140 case U'X':
142 default:
144 }
145 };
146 }
147
149
151 using namespace integral_set_literals;
152 return (parser::match_one('b'_m || 'B'_m || 'c'_m || 'd'_m || 'o'_m || 'x'_m || 'X'_m || '?'_m)) %
153 [](char32_t ch) {
154 switch (ch) {
155 case U'b':
157 case U'B':
159 case U'c':
161 case U'd':
163 case U'o':
165 case U'x':
167 case U'X':
169 case U'?':
171 default:
173 }
174 };
175 }
176
178
180 using namespace integral_set_literals;
181 return (-parser::match_one('b'_m || 'B'_m || 'd'_m || 'o'_m || 's'_m || 'x'_m || 'X'_m)) %
182 [](Optional<char32_t> ch) {
183 switch (ch.value_or(U's')) {
184 case U'b':
186 case U'B':
188 case U'd':
189 return BoolType::Decimal;
190 case U'o':
191 return BoolType::Octal;
192 case U's':
193 return BoolType::String;
194 case U'x':
195 return BoolType::HexLower;
196 case U'X':
197 return BoolType::HexUpper;
198 default:
200 }
201 };
202 }
203
204 enum class PointerType { HexLower };
205
207 using namespace integral_set_literals;
208 return (-parser::match_one('p'_m)) % [](Optional<char32_t> ch) {
209 switch (ch.value_or(U'p')) {
210 case U'p':
212 default:
214 }
215 };
216 }
217
231
247
263
279
291
292 template<concepts::Encoding Enc>
293 constexpr auto present_string_view_to(concepts::FormatContext auto& context, Optional<FillAndAlign> fill_and_align,
294 Optional<size_t> width, Optional<size_t> precision, bool debug,
296 char32_t delimit_code_point = U'"') -> Result<void> {
297 using CodePoint = meta::EncodingCodePoint<Enc>;
298
299 auto delimit = lift_bool(debug) % function::value(delimit_code_point);
300
301 auto map_to_debug_char = [&](CodePoint p) -> di::StaticVector<c32, di::Constexpr<4>> {
303 if (!debug) {
304 (void) result.push_back(c32(p));
305 } else {
306 // Special escaped chars. These are represented as \P.
307 if (c32(p) == '\0') {
308 (void) result.push_back(U'\\');
309 (void) result.push_back(U'0');
310 } else if (c32(p) == '\a') {
311 (void) result.push_back(U'\\');
312 (void) result.push_back(U'a');
313 } else if (c32(p) == '\b') {
314 (void) result.push_back(U'\\');
315 (void) result.push_back(U'b');
316 } else if (c32(p) == '\f') {
317 (void) result.push_back(U'\\');
318 (void) result.push_back(U'f');
319 } else if (c32(p) == '\n') {
320 (void) result.push_back(U'\\');
321 (void) result.push_back(U'n');
322 } else if (c32(p) == '\r') {
323 (void) result.push_back(U'\\');
324 (void) result.push_back(U'r');
325 } else if (c32(p) == '\t') {
326 (void) result.push_back(U'\\');
327 (void) result.push_back(U't');
328 } else if (c32(p) == '\v') {
329 (void) result.push_back(U'\\');
330 (void) result.push_back(U'v');
331 } else if (c32(p) == '\\') {
332 (void) result.push_back(U'\\');
333 (void) result.push_back(U'\\');
334 }
335 // The delimiter is also escaped. For instance: " will become \".
336 else if (c32(p) == delimit_code_point) {
337 (void) result.push_back(U'\\');
338 (void) result.push_back(delimit_code_point);
339 }
340 // Control characters should not be printed directly in debug mode.
341 // This range accounts for C0 and C1 Unicode control characters.
342 else if (p <= 31 || p == 127 || (p >= 0x80 && p <= 0x9F)) {
343 auto v1 = (u8(p) / 16) & 0xF;
344 auto v2 = (u8(p) % 16);
345
346 (void) result.push_back(U'\\');
347 (void) result.push_back(U'x');
348 (void) result.push_back((v1 >= 10) ? ('a' + (v1 - 10)) : ('0' + v1));
349 (void) result.push_back((v2 >= 10) ? ('a' + (v2 - 10)) : ('0' + v2));
350 } else {
351 (void) result.push_back(c32(p));
352 }
353 }
354 return result;
355 };
356
357 auto view = view::concat(delimit, view::join(view::transform(view_in, map_to_debug_char)), delimit);
358
359 auto measure_code_point = [&](CodePoint) -> size_t {
360 // TODO: use east asian width field.
361 // TODO: measure grapheme clusters instead of measuring width code point by code point.
362 return 1;
363 };
364
365 auto do_output_char = [&](CodePoint code_point) -> Result<void> {
366 (void) context.output(code_point);
367 return {};
368 };
369
370 size_t width_printed_so_far = 0;
371
372 auto output_char = [&](CodePoint code_point) -> Result<void> {
373 // Fast path when there is no precision.
374 if (!precision) {
375 return do_output_char(code_point);
376 }
377
378 // Don't print characters after the precision is exceeded.
379 auto code_point_width = measure_code_point(code_point);
380 if (width_printed_so_far + code_point_width > *precision) {
381 return {};
382 }
383
384 auto result = do_output_char(code_point);
385 width_printed_so_far += code_point_width;
386 return result;
387 };
388
389 if (!width) {
390 return container::sequence(view, output_char);
391 }
392
393 auto total_width = view | view::transform(measure_code_point) | container::sum;
394 if (total_width >= *width) {
395 return container::sequence(view, output_char);
396 }
397
398 auto align = fill_and_align.transform(&FillAndAlign::align).value_or(FillAndAlign::Align::Left);
399 auto fill_code_point = fill_and_align.transform(&FillAndAlign::fill).value_or(U' ');
400
401 auto chars_to_pad = (*width - total_width) / measure_code_point(fill_code_point);
402 auto [left_pad, right_pad] = [&]() -> Tuple<size_t, size_t> {
403 switch (align) {
405 return { 0, chars_to_pad };
407 return { chars_to_pad / 2, math::divide_round_up(chars_to_pad, 2U) };
409 return { chars_to_pad, 0 };
410 default:
412 }
413 }();
414
415 auto do_pad = [&](auto) {
416 return output_char(fill_code_point);
417 };
418
419 DI_TRY(container::sequence(view::range(left_pad), do_pad));
420 DI_TRY(container::sequence(view, output_char));
421 return container::sequence(view::range(right_pad), do_pad);
422 }
423
424 template<concepts::Encoding Enc>
425 constexpr auto present_character_to(concepts::FormatContext auto& context, Optional<FillAndAlign> fill_and_align,
426 Optional<size_t> width, bool debug, c32 value) -> Result<void> {
427 auto encoding = context.encoding();
428 auto as_code_units = container::string::encoding::convert_to_code_units(encoding, value);
429 auto [first, last] =
430 container::string::encoding::code_point_view(encoding, { as_code_units.data(), as_code_units.size() });
431 auto as_string_view = container::string::StringViewImpl<Enc> { first, last, encoding };
432 return present_string_view_to(context, fill_and_align, width, nullopt, debug, as_string_view, U'\'');
433 }
434
435 template<concepts::Encoding Enc, concepts::Integral T>
436 constexpr auto present_integer_to(concepts::FormatContext auto& context, Optional<FillAndAlign> fill_and_align,
437 Sign sign, HashTag hash_tag, Zero zero, Optional<size_t> width, IntegerType type,
438 bool debug, T value) -> Result<void> {
439 if (type == IntegerType::Character) {
440 return present_character_to<Enc>(context, fill_and_align, width, debug, static_cast<c32>(value));
441 }
442
443 using CodePoint = meta::EncodingCodePoint<Enc>;
444
445 // The maximum number of digits a number can have is 64 (u64::max() printed in binary).
446 // Add 3 extra characters to account for a prefix, like -0x.
447 auto buffer = container::string::StringImpl<
449
450 using UnsignedType = meta::MakeUnsigned<T>;
451 auto as_unsigned = math::abs_unsigned(value);
452
453 auto const negative = [&] {
454 if constexpr (concepts::Signed<T>) {
455 return value < 0;
456 }
457 return false;
458 }();
459
460 auto do_sign = [&] {
461 switch (sign) {
462 case Sign::Minus:
463 if (negative) {
464 (void) buffer.push_back(CodePoint('-'));
465 }
466 break;
467 case Sign::Plus:
468 if (negative) {
469 (void) buffer.push_back(CodePoint('-'));
470 } else {
471 (void) buffer.push_back(CodePoint('+'));
472 }
473 break;
474 case Sign::Space:
475 if (negative) {
476 (void) buffer.push_back(CodePoint('-'));
477 } else {
478 (void) buffer.push_back(CodePoint(' '));
479 }
480 break;
481 }
482 };
483
484 auto do_prefix = [&] {
485 if (hash_tag == HashTag::No) {
486 return;
487 }
488 switch (type) {
490 (void) buffer.push_back(CodePoint('0'));
491 (void) buffer.push_back(CodePoint('b'));
492 break;
494 (void) buffer.push_back(CodePoint('0'));
495 (void) buffer.push_back(CodePoint('B'));
496 break;
498 (void) buffer.push_back(CodePoint('0'));
499 break;
501 (void) buffer.push_back(CodePoint('0'));
502 (void) buffer.push_back(CodePoint('x'));
503 break;
505 (void) buffer.push_back(CodePoint('0'));
506 (void) buffer.push_back(CodePoint('X'));
507 break;
508 default:
509 break;
510 }
511 };
512
513 bool zero_pad = zero == Zero::Yes && !fill_and_align;
514
515 do_sign();
516 do_prefix();
517
518 if (zero_pad) {
519 for (auto ch : buffer) {
520 context.output(ch);
521 }
522 auto code_points = math::to_unsigned(container::distance(buffer));
523 if (width && *width > code_points) {
524 *width -= code_points;
525 }
526 buffer.clear();
527 }
528
529 auto const radix = [&] -> UnsignedType {
530 switch (type) {
533 return 2;
535 return 8;
538 return 16;
539 default:
540 return 10;
541 }
542 }();
543
544 auto to_digit = [&](UnsignedType value) -> meta::EncodingCodePoint<Enc> {
545 switch (type) {
547 return (value >= 10) ? ('a' + (value - 10)) : ('0' + value);
549 return (value >= 10) ? ('A' + (value - 10)) : ('0' + value);
550 default:
551 return ('0' + value);
552 }
553 };
554
555 UnsignedType strength = 1;
556 for (auto x = as_unsigned; x / strength >= radix; strength *= radix) {}
557
558 for (; strength; strength /= radix) {
559 (void) buffer.push_back(to_digit((as_unsigned / strength) % radix));
560 }
561
562 auto backup_fill_and_align = FillAndAlign { zero_pad ? U'0' : U' ', FillAndAlign::Align::Right };
563 return present_string_view_to<Enc>(context, fill_and_align.value_or(backup_fill_and_align), width, nullopt,
564 false, buffer);
565 }
566
567 template<concepts::Encoding Enc>
568 constexpr auto present_formatted_to(concepts::FormatContext auto& context, Optional<FillAndAlign> fill_and_align,
570 container::string::StringViewImpl<Enc> format_string, auto&&... args)
571 -> Result<void> {
572 // If there is no width, fill_and_align is ignored, so no temporary buffer is needed.
573
574 // Precision in this case refers to the upper bound on the text width to be printed.
575 // Handle precision using a modified context which caps to number of characters printed,
576 // in the case where there is no width.
577
578 // If width is present, we must first collect the input into a temporary buffer before presenting.
579 // Performing 2 passes is not viable, as we may be printing InputContainers, which are only valid
580 // for a single pass, and anyway, iterating over a large vector twice should be avoided.
581
582 // The temporary buffer should include a sizable inline capacity (~256 bytes), to prevent heap
583 // allocation except for in extreme cases. Note that if the width is smaller than 256, formatting
584 // can be successful without ever allocating. However, the temporary buffer should be customizable,
585 // so that in kernel context, allocation can never happen (and perhaps less stack size should be
586 // used, since the kernel may only have 4096 bytes of stack space).
587
589 DI_ASSERT(!precision);
590 (void) fill_and_align;
591
592 return vpresent_encoded_context<Enc>(context, format_string,
593 format::make_format_args<decltype(context)>(args...));
594 }
595}
596}
#define DI_ASSERT(...)
Definition assert_bool.h:7
Definition static_vector.h:17
Definition string_impl_forward_declaration.h:9
Definition string_view_impl_forward_declaration.h:7
Definition optional_forward_declaration.h:5
Definition tuple_forward_declaration.h:5
Definition format_context.h:9
Definition language.h:238
Definition tuple_like.h:38
constexpr usize width
Definition gfx_test.cpp:21
#define DI_TRY(...)
Definition monad_try.h:13
constexpr auto convert_to_code_units
Definition encoding.h:139
constexpr auto code_point_view
Definition encoding.h:159
constexpr auto sequence
Definition sequence.h:34
constexpr auto distance
Definition distance.h:44
constexpr auto sum
Definition sum.h:26
Definition base.h:16
IntegerType
Definition base.h:121
@ BinaryLower
Definition base.h:121
@ HexLower
Definition base.h:121
@ HexUpper
Definition base.h:121
@ Character
Definition base.h:121
@ BinaryUpper
Definition base.h:121
@ Octal
Definition base.h:121
@ Decimal
Definition base.h:121
BoolType
Definition base.h:177
@ String
Definition base.h:177
@ BinaryLower
Definition base.h:177
@ HexLower
Definition base.h:177
@ HexUpper
Definition base.h:177
@ BinaryUpper
Definition base.h:177
@ Octal
Definition base.h:177
@ Decimal
Definition base.h:177
Sign
Definition base.h:47
@ Plus
Definition base.h:47
@ Minus
Definition base.h:47
@ Space
Definition base.h:47
CharacterType
Definition base.h:148
@ BinaryLower
Definition base.h:148
@ HexLower
Definition base.h:148
@ HexUpper
Definition base.h:148
@ Character
Definition base.h:148
@ BinaryUpper
Definition base.h:148
@ Octal
Definition base.h:148
@ Decimal
Definition base.h:148
@ Debug
Definition base.h:148
HashTag
Definition base.h:65
@ Yes
Definition base.h:65
@ No
Definition base.h:65
constexpr auto present_string_view_to(concepts::FormatContext auto &context, Optional< FillAndAlign > fill_and_align, Optional< size_t > width, Optional< size_t > precision, bool debug, container::string::StringViewImpl< Enc > view_in, char32_t delimit_code_point=U'"') -> Result<void>
Definition base.h:293
StringType
Definition base.h:105
@ String
Definition base.h:105
@ Debug
Definition base.h:105
PointerType
Definition base.h:204
@ HexLower
Definition base.h:204
Zero
Definition base.h:74
@ Yes
Definition base.h:74
@ No
Definition base.h:74
constexpr auto present_formatted_to(concepts::FormatContext auto &context, Optional< FillAndAlign > fill_and_align, Optional< size_t > width, Optional< size_t > precision, container::string::StringViewImpl< Enc > format_string, auto &&... args) -> Result< void >
Definition base.h:568
constexpr auto present_character_to(concepts::FormatContext auto &context, Optional< FillAndAlign > fill_and_align, Optional< size_t > width, bool debug, c32 value) -> Result< void >
Definition base.h:425
constexpr auto present_integer_to(concepts::FormatContext auto &context, Optional< FillAndAlign > fill_and_align, Sign sign, HashTag hash_tag, Zero zero, Optional< size_t > width, IntegerType type, bool debug, T value) -> Result< void >
Definition base.h:436
Definition bounded_format_context.h:7
constexpr auto make_format_args(Types &&... values)
Definition make_format_args.h:9
constexpr auto vpresent_encoded_context
Definition vpresent_encoded_context.h:61
constexpr auto value
Definition value.h:34
Definition integral_set.h:125
constexpr auto to_unsigned
Definition to_unsigned.h:16
constexpr auto divide_round_up
Definition divide_round_up.h:17
constexpr auto abs_unsigned
Definition abs_unsigned.h:26
RemoveCVRef< T >::CodePoint EncodingCodePoint
Definition encoding.h:19
detail::MakeUnsignedHelper< RemoveCV< T > >::Type MakeUnsigned
Definition language.h:362
constexpr auto match_one
Definition match_one.h:27
constexpr auto integer
Definition integer.h:212
__UINT8_TYPE__ u8
Definition integers.h:9
char32_t c32
Definition char.h:6
di::meta::Decay< decltype(T)> Tag
Definition tag_invoke.h:28
void unreachable()
Definition unreachable.h:4
Expected< T, Error > Result
Definition result.h:8
constexpr auto create_parser
Definition create_parser.h:55
constexpr tag_invoke_detail::TagInvokeFn tag_invoke
Definition tag_invoke.h:22
constexpr auto make_from_tuple
Definition make_from_tuple.h:31
constexpr auto nullopt
Definition nullopt.h:15
constexpr auto lift_bool
Definition lift_bool.h:13
Definition base.h:264
Optional< FillAndAlign > fill_and_align
Definition base.h:265
Zero zero
Definition base.h:268
HashTag hash_tag
Definition base.h:267
Sign sign
Definition base.h:266
Optional< Width > width
Definition base.h:269
BoolType type
Definition base.h:270
constexpr friend auto tag_invoke(types::Tag< create_parser_in_place >, InPlaceType< BoolFormat >)
Definition base.h:273
Optional< CharacterType > type
Definition base.h:254
Zero zero
Definition base.h:252
HashTag hash_tag
Definition base.h:251
Sign sign
Definition base.h:250
Optional< Width > width
Definition base.h:253
constexpr friend auto tag_invoke(types::Tag< create_parser_in_place >, InPlaceType< CharacterFormat >)
Definition base.h:257
Optional< FillAndAlign > fill_and_align
Definition base.h:249
constexpr friend auto tag_invoke(types::Tag< create_parser_in_place >, InPlaceType< FillAndAlign >)
Definition base.h:24
Align align
Definition base.h:21
char32_t fill
Definition base.h:20
constexpr friend auto tag_invoke(types::Tag< create_parser_in_place >, InPlaceType< IntegerFormat >)
Definition base.h:241
HashTag hash_tag
Definition base.h:235
Sign sign
Definition base.h:234
IntegerType type
Definition base.h:238
Zero zero
Definition base.h:236
Optional< Width > width
Definition base.h:237
Optional< FillAndAlign > fill_and_align
Definition base.h:233
PointerType type
Definition base.h:283
constexpr friend auto tag_invoke(types::Tag< create_parser_in_place >, InPlaceType< PointerFormat >)
Definition base.h:286
Optional< Width > width
Definition base.h:282
Optional< FillAndAlign > fill_and_align
Definition base.h:281
Definition base.h:94
constexpr friend auto tag_invoke(types::Tag< create_parser_in_place >, InPlaceType< Precision >)
Definition base.h:98
size_t value
Definition base.h:95
Optional< Width > width
Definition base.h:220
constexpr friend auto tag_invoke(types::Tag< create_parser_in_place >, InPlaceType< StringFormat >)
Definition base.h:225
Optional< StringType > type
Definition base.h:222
Optional< FillAndAlign > fill_and_align
Definition base.h:219
Optional< Precision > precision
Definition base.h:221
Definition base.h:83
constexpr friend auto tag_invoke(types::Tag< create_parser_in_place >, InPlaceType< Width >)
Definition base.h:87
size_t value
Definition base.h:84
A wrapper for a constexpr value.
Definition core.h:77
Definition in_place_type.h:5