Embedded Template Library 1.0
Loading...
Searching...
No Matches
format.h
Go to the documentation of this file.
1
2
3/******************************************************************************
4The MIT License(MIT)
5
6Embedded Template Library.
7https://github.com/ETLCPP/etl
8https://www.etlcpp.com
9
10Copyright(c) 2025 BMW AG
11
12Permission is hereby granted, free of charge, to any person obtaining a copy
13of this software and associated documentation files(the "Software"), to deal
14in the Software without restriction, including without limitation the rights
15to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
16copies of the Software, and to permit persons to whom the Software is
17furnished to do so, subject to the following conditions :
18
19The above copyright notice and this permission notice shall be included in all
20copies or substantial portions of the Software.
21
22THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
25AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28SOFTWARE.
29******************************************************************************/
30
31#ifndef ETL_FORMAT_INCLUDED
32#define ETL_FORMAT_INCLUDED
33
34#include "platform.h"
35
36#include "algorithm.h"
37#include "array.h"
38#include "array_view.h"
39#include "error_handler.h"
40#include "limits.h"
41#include "math.h"
42#include "optional.h"
43#include "span.h"
44#include "string.h"
45#include "string_view.h"
46#include "type_traits.h"
47#include "utility.h"
48#include "variant.h"
49#include "visitor.h"
50
51#if ETL_USING_FORMAT_FLOATING_POINT
52 #include <cmath>
53#endif
54
55#if ETL_USING_CPP11
56
57namespace etl
58{
59 class format_exception : public etl::exception
60 {
61 public:
62
63 format_exception(string_type reason_, string_type file_name_, numeric_type line_number_)
64 : exception(reason_, file_name_, line_number_)
65 {
66 }
67 };
68
69 class bad_format_string_exception : public etl::format_exception
70 {
71 public:
72
73 bad_format_string_exception(string_type file_name_, numeric_type line_number_)
74 : etl::format_exception(ETL_ERROR_TEXT("format:bad", ETL_FORMAT_FILE_ID"A"), file_name_, line_number_)
75 {
76 }
77 };
78
79 template <class... Args>
80 ETL_CONSTEXPR14 bool check_f(const char* fmt)
81 {
82 // to be implemented later
83 // return fmt[0] == 0; // actual check
84
85 (void)fmt;
86 return true;
87 }
88
89 inline void please_note_this_is_error_message_1() noexcept {}
90
91 template <class... Args>
92 struct basic_format_string
93 {
94 inline ETL_CONSTEVAL basic_format_string(const char* fmt)
95 : _sv(fmt)
96 {
97 bool format_string_ok = check_f(fmt);
98
99 if (!format_string_ok)
100 {
101 // if (etl::is_constant_evaluated()) // compile time error path
102 //{
103 // // calling a non-constexpr function in a consteval context to
104 // trigger a compile error please_note_this_is_error_message_1();
105 // }
106 // else // run time error path
107 //{
108 ETL_ASSERT_FAIL_AND_RETURN(ETL_ERROR(bad_format_string_exception));
109 //}
110 }
111 }
112
113 ETL_CONSTEXPR basic_format_string(const basic_format_string& other) = default;
114 ETL_CONSTEXPR14 basic_format_string& operator=(const basic_format_string& other) = default;
115
116 ETL_CONSTEXPR string_view get() const
117 {
118 return _sv;
119 }
120
121 private:
122
123 string_view _sv;
124 };
125
126 template <class... Args>
127 using format_string = basic_format_string<type_identity_t<Args>...>;
128
129 // Supported types to format
130 //
131 // This is the limited number of types as defined in std::basic_format_arg
132 // https://en.cppreference.com/w/cpp/utility/format/basic_format_arg.html
133 //
134 // Further types to be supported are added via converting constructors in
135 // etl::basic_format_arg
136 using supported_format_types = etl::variant< etl::monostate, bool, char, int, unsigned int, long long int, unsigned long long int,
137 #if ETL_USING_FORMAT_FLOATING_POINT
138 float, double, long double,
139 #endif
140 const char*, etl::string_view, const void*
141 // basic_format_arg::handle,
142 >;
143
144 template <class CharT>
145 class basic_format_parse_context
146 {
147 public:
148
149 using iterator = string_view::const_iterator;
150 using const_iterator = string_view::const_iterator;
151 using char_type = CharT;
152
153 basic_format_parse_context(etl::string_view fmt, size_t n_args = 0)
154 : range(fmt)
155 , num_args(n_args)
156 , current(0)
157 , automatic_mode(false)
158 , manual_mode(false)
159 {
160 }
161
162 basic_format_parse_context<CharT>& operator=(const basic_format_parse_context&) = delete;
163
164 iterator begin() const noexcept
165 {
166 return range.begin();
167 }
168
169 iterator end() const noexcept
170 {
171 return range.end();
172 }
173
174 ETL_CONSTEXPR14 void advance_to(iterator pos)
175 {
176 range = etl::string_view(pos, range.end());
177 }
178
179 ETL_CONSTEXPR14 size_t next_arg_id()
180 {
181 // automatic number generation only allowed if not already in manual mode
182 ETL_ASSERT(manual_mode == false, ETL_ERROR(bad_format_string_exception));
183 automatic_mode = true;
184 // TODO: compile time check
185 ETL_ASSERT(current < num_args, ETL_ERROR(bad_format_string_exception) /* not enough arguments for generated index */);
186 return current++;
187 }
188
189 ETL_CONSTEXPR14 void check_arg_id(size_t id)
190 {
191 // manual index specification only allowed if not already in automatic
192 // mode
193 ETL_ASSERT(automatic_mode == false, ETL_ERROR(bad_format_string_exception));
194 manual_mode = true;
195 ETL_ASSERT(id < num_args, ETL_ERROR(bad_format_string_exception) /* index out of range */);
196 }
197
198 private:
199
200 etl::string_view range;
201 size_t num_args;
202 size_t current;
203 bool automatic_mode;
204 bool manual_mode;
205
206 template <class, class>
207 friend struct formatter;
208 };
209
210 using format_parse_context = basic_format_parse_context<char>;
211
212 template <class Context>
213 class basic_format_arg
214 {
215 public:
216
217 class handle
218 {
219 public:
220
221 void format(etl::basic_format_parse_context<char>& /* parse_ctx */, Context& /*format_ctx*/)
222 {
223 // typename Context::template formatter_type<TD> f;
224 // parse_ctx.advance_to(f.parse(parse_ctx));
225 // format_ctx.advance_to(f.format(const_cast<TQ&>(static_cast<const
226 // TD&>(ref)), format_ctx));
227 }
228
229 private:
230
231 const void* obj;
232 typedef void (*function_type)(etl::basic_format_parse_context<char>&, Context&, const void*);
233 function_type func;
234 };
235
236 basic_format_arg() {}
237
238 basic_format_arg(const bool v)
239 : data(v)
240 {
241 }
242
243 basic_format_arg(const int v)
244 : data(v)
245 {
246 }
247
248 basic_format_arg(const short v)
249 : data(static_cast<int>(v))
250 {
251 }
252
253 basic_format_arg(const unsigned short v)
254 : data(static_cast<unsigned int>(v))
255 {
256 }
257
258 basic_format_arg(const long int v)
259 : data(static_cast<long long int>(v))
260 {
261 }
262
263 basic_format_arg(const unsigned int v)
264 : data(v)
265 {
266 }
267
268 basic_format_arg(const long long int v)
269 : data(v)
270 {
271 }
272
273 basic_format_arg(const unsigned long long int v)
274 : data(v)
275 {
276 }
277
278 // Additional type to list of basic types as defined for
279 // std::basic_format_arg: Mapping unsigned long to unsigned long long int
280 basic_format_arg(const unsigned long v)
281 : data(static_cast<unsigned long long int>(v))
282 {
283 }
284
285 basic_format_arg(const char* v)
286 : data(v)
287 {
288 }
289
290 basic_format_arg(char v)
291 : data(v)
292 {
293 }
294
295 basic_format_arg(const signed char v)
296 : data(static_cast<char>(v))
297 {
298 }
299
300 basic_format_arg(const unsigned char v)
301 : data(static_cast<char>(v))
302 {
303 }
304
305 #if ETL_USING_FORMAT_FLOATING_POINT
306 basic_format_arg(const float v)
307 : data(v)
308 {
309 }
310
311 basic_format_arg(const double v)
312 : data(v)
313 {
314 }
315
316 basic_format_arg(const long double v)
317 : data(v)
318 {
319 }
320 #endif
321
322 basic_format_arg(const etl::string_view v)
323 : data(v)
324 {
325 }
326
327 basic_format_arg(const etl::ibasic_string<char>& v)
328 : data(etl::string_view(v.data(), v.size()))
329 {
330 }
331
332 basic_format_arg(const basic_format_arg& other)
333 : data(other.data)
334 {
335 }
336
337 basic_format_arg(const void* v)
338 : data(v)
339 {
340 }
341
342 basic_format_arg& operator=(const basic_format_arg& other)
343 {
344 data = other.data;
345 return *this;
346 }
347
348 explicit operator bool() const
349 {
350 return !etl::holds_alternative<etl::monostate>(data);
351 }
352
353 template <class R, class Visitor>
354 R visit(Visitor&& vis)
355 {
356 return etl::visit(etl::forward<Visitor>(vis), data);
357 }
358
359 private:
360
361 supported_format_types data;
362 };
363
364 template <class Context, class... Args>
365 class format_arg_store
366 {
367 public:
368
369 format_arg_store(Args&... args)
370 : _args{args...}
371 {
372 }
373
374 basic_format_arg<Context> get(size_t i) const
375 {
376 return _args.get(i);
377 }
378
379 etl::array_view<basic_format_arg<Context>> get()
380 {
381 return _args;
382 }
383
384 private:
385
386 etl::array<basic_format_arg<Context>, sizeof...(Args)> _args;
387 };
388
389 template <class Context>
390 class basic_format_args
391 {
392 public:
393
394 template <class... Args>
395 basic_format_args(format_arg_store<Context, Args...>& store)
396 : _args(store.get())
397 {
398 }
399
400 basic_format_args(const basic_format_args<Context>& other)
401 : _args(other._args)
402 {
403 }
404
405 basic_format_args& operator=(const basic_format_args<Context>& other)
406 {
407 _args = other._args;
408 return *this;
409 }
410
411 basic_format_arg<Context> get(size_t i) const
412 {
413 return _args[i];
414 }
415
416 // non-standard
417 size_t size()
418 {
419 return _args.size();
420 }
421
422 private:
423
424 etl::array_view<basic_format_arg<Context>> _args;
425 };
426
427 namespace private_format
428 {
429 using char_type = char;
430
431 enum class spec_align_t
432 {
433 NONE, // default
434 START,
435 END,
436 CENTER
437 };
438
439 enum class spec_sign_t
440 {
441 MINUS, // default
442 PLUS,
443 SPACE
444 };
445
446 struct format_spec_t
447 {
448 etl::optional<size_t> index{etl::nullopt_t()};
449 spec_align_t align{spec_align_t::NONE}; // '<' / '>' / '^' / none (default)
450 char_type fill{' '}; // fill character (' ' is default)
451 spec_sign_t sign{spec_sign_t::MINUS}; // '+' / '-' (default) / ' '
452 bool hash{false}; // #
453 bool zero{false}; // 0
454 etl::optional<size_t> width{etl::nullopt_t()}; // the arg index if width_nested_replacement == true
455 bool width_nested_replacement{false}; // {}
456 etl::optional<size_t> precision{etl::nullopt_t()}; // the arg index if
457 // precision_nested_replacement == true
458 bool precision_nested_replacement{false}; // {}
459 bool locale_specific{false}; // 'L'
460 etl::optional<char> type{etl::nullopt_t()}; // literal 's', 'b', 'd', ...
461 };
462 } // namespace private_format
463
464 template <class OutputIt, class CharT>
465 class basic_format_context
466 {
467 public:
468
469 using iterator = OutputIt;
470 using char_type = CharT;
471
472 basic_format_context(const basic_format_context& other)
473 : _it(other._it)
474 , _format_args(other._format_args)
475 {
476 }
477
478 basic_format_context(OutputIt it, basic_format_args<basic_format_context>& fmt_args)
479 : _it(it)
480 , _format_args(fmt_args)
481 {
482 }
483
484 basic_format_context& operator=(const basic_format_context&) = delete;
485
486 basic_format_arg<basic_format_context> arg(size_t id) const
487 {
488 return _format_args.get(id);
489 }
490
491 iterator out()
492 {
493 return _it;
494 }
495
496 void advance_to(iterator it)
497 {
498 _it = it;
499 }
500
501 private_format::format_spec_t format_spec;
502
503 private:
504
505 iterator _it;
506 basic_format_args<basic_format_context>& _format_args;
507 };
508
509 template <class OutputIt>
510 using format_context = basic_format_context<OutputIt, char>;
511
512 template <class OutputIt>
513 using format_args = basic_format_args<format_context<OutputIt>>;
514
515 template <class OutputIt>
516 using format_arg = basic_format_arg<format_context<OutputIt>>;
517
518 template <class OutputIt, class Context = format_context<OutputIt>, class... Args>
519 format_arg_store<Context, Args...> make_format_args(Args&... args)
520 {
521 return format_arg_store<Context, Args...>(args...);
522 }
523
524 namespace private_format
525 {
526 inline bool is_digit(const char c)
527 {
528 return c >= '0' && c <= '9';
529 }
530
531 inline void advance(format_parse_context& parse_ctx)
532 {
533 parse_ctx.advance_to(parse_ctx.begin() + 1);
534 }
535
536 inline etl::optional<size_t> parse_num(format_parse_context& parse_ctx)
537 {
538 etl::optional<size_t> result;
539 auto fmt_it = parse_ctx.begin();
540 while (fmt_it != parse_ctx.end())
541 {
542 const char c = *fmt_it;
543 if (is_digit(c))
544 {
545 size_t old_value = result.value_or(0);
546 size_t new_value = old_value * 10 + static_cast<size_t>(c - '0');
547 if (new_value < old_value)
548 {
549 // Overflow detected
550 ETL_ASSERT_FAIL(ETL_ERROR(bad_format_string_exception));
551 }
552 result = new_value;
553 }
554 else
555 {
556 break;
557 }
558 ++fmt_it;
559 }
560 if (result.has_value())
561 {
562 parse_ctx.advance_to(fmt_it);
563 }
564 return result;
565 }
566
567 inline etl::optional<char> parse_any_of(format_parse_context& parse_ctx, etl::string_view chars)
568 {
569 etl::optional<char> result;
570 auto fmt_it = parse_ctx.begin();
571 if (fmt_it != parse_ctx.end())
572 {
573 const char c = *fmt_it;
574 auto it = etl::find(chars.cbegin(), chars.cend(), c);
575 if (it != chars.cend())
576 {
577 result = *it;
578 ++fmt_it;
579 parse_ctx.advance_to(fmt_it);
580 }
581 }
582 return result;
583 }
584
585 inline bool parse_char(format_parse_context& parse_ctx, char c)
586 {
587 auto fmt_it = parse_ctx.begin();
588 if (fmt_it != parse_ctx.end())
589 {
590 char value = *fmt_it;
591 if (value == c)
592 {
593 ++fmt_it;
594 parse_ctx.advance_to(fmt_it);
595 return true;
596 }
597 }
598 return false;
599 }
600
601 inline bool parse_sequence(format_parse_context& parse_ctx, etl::string_view sequence)
602 {
603 auto fmt_it = parse_ctx.begin();
604 if (etl::equal(sequence.cbegin(), sequence.cend(), fmt_it))
605 {
606 fmt_it += sequence.size();
607 parse_ctx.advance_to(fmt_it);
608 return true;
609 }
610 return false;
611 }
612
613 inline bool is_align_character(char c)
614 {
615 return c == '<' || c == '>' || c == '^';
616 }
617
618 inline spec_align_t align_from_char(char c)
619 {
620 spec_align_t result = spec_align_t::NONE;
621 switch (c)
622 {
623 case '<': result = spec_align_t::START; break;
624 case '>': result = spec_align_t::END; break;
625 case '^': result = spec_align_t::CENTER; break;
626 default:
627 // invalid alignment specification
628 ETL_ASSERT_FAIL(ETL_ERROR(bad_format_string_exception));
629 }
630 return result;
631 }
632
633 inline spec_align_t parse_fill_and_align(format_parse_context& parse_ctx, char_type& fill)
634 {
635 spec_align_t result = spec_align_t::NONE;
636 fill = ' '; // default
637
638 auto fmt_it = parse_ctx.begin();
639 if (fmt_it != parse_ctx.end())
640 {
641 const char c = *fmt_it;
642 ++fmt_it;
643
644 if (is_align_character(c))
645 {
646 result = align_from_char(c);
647 parse_ctx.advance_to(fmt_it);
648 }
649 else if (fmt_it != parse_ctx.end())
650 {
651 const char c2 = *fmt_it;
652 ++fmt_it;
653 if (is_align_character(c2))
654 {
655 result = align_from_char(c2);
656 ETL_ASSERT(c != '{' && c != '}',
657 ETL_ERROR(bad_format_string_exception)); // no { or } allowed as
658 // fill character
659 fill = c;
660 parse_ctx.advance_to(fmt_it);
661 }
662 }
663 else
664 {
665 // no align and fill spec (valid)
666 }
667 }
668 return result;
669 }
670
671 inline spec_sign_t sign_from_char(const char c)
672 {
673 spec_sign_t result = spec_sign_t::MINUS;
674 switch (c)
675 {
676 case '-': result = spec_sign_t::MINUS; break;
677 case '+': result = spec_sign_t::PLUS; break;
678 case ' ': result = spec_sign_t::SPACE; break;
679 default:
680 // invalid sign character c
681 ETL_ASSERT_FAIL(ETL_ERROR(bad_format_string_exception));
682 }
683 return result;
684 }
685
686 inline bool parse_nested_replacement(format_parse_context& parse_ctx, etl::optional<size_t>& index)
687 {
688 bool start = parse_char(parse_ctx, '{');
689 if (start)
690 {
691 auto num = parse_num(parse_ctx);
692 if (num)
693 {
694 // manual mode
695 index = num;
696 parse_ctx.check_arg_id(*index);
697 bool end = parse_char(parse_ctx, '}');
698 if (end)
699 {
700 return true;
701 }
702 else
703 {
704 // bad nested replacement index spec
705 ETL_ASSERT_FAIL(ETL_ERROR(bad_format_string_exception));
706 }
707 }
708 else
709 {
710 bool end = parse_char(parse_ctx, '}');
711 if (end)
712 {
713 index = parse_ctx.next_arg_id();
714 return true;
715 }
716 else
717 {
718 // bad nested replacement spec
719 ETL_ASSERT_FAIL(ETL_ERROR(bad_format_string_exception));
720 }
721 }
722 }
723 return false;
724 }
725
726 template <class OutputIt>
727 void parse_format_spec(format_parse_context& parse_ctx, format_context<OutputIt>& fmt_context)
728 {
729 auto& format_spec = fmt_context.format_spec;
730
731 format_spec = format_spec_t(); // reset format_spec to defaults
732
733 format_spec.index = parse_num(parse_ctx); // optional
734
735 bool colon = parse_char(parse_ctx, ':');
736 if (colon)
737 {
738 format_spec.align = parse_fill_and_align(parse_ctx, format_spec.fill);
739
740 etl::optional<char> sign = parse_any_of(parse_ctx, "+- ");
741 if (sign)
742 {
743 format_spec.sign = sign_from_char(*sign);
744 }
745
746 format_spec.hash = parse_char(parse_ctx, '#');
747 format_spec.zero = parse_char(parse_ctx, '0');
748
749 format_spec.width = parse_num(parse_ctx);
750 if (!format_spec.width)
751 {
752 // possibly with via nested replacement
753 format_spec.width_nested_replacement = parse_nested_replacement(parse_ctx, format_spec.width);
754 }
755
756 if (parse_char(parse_ctx, '.'))
757 {
758 format_spec.precision = parse_num(parse_ctx);
759 if (!format_spec.precision)
760 {
761 // possibly with via nested replacement
762 format_spec.precision_nested_replacement = parse_nested_replacement(parse_ctx, format_spec.precision);
763 }
764 }
765
766 format_spec.locale_specific = parse_char(parse_ctx, 'L');
767
768 format_spec.type = parse_any_of(parse_ctx, "s?bBcdoxXaAeEfFgGpP");
769 }
770 }
771 } // namespace private_format
772
773 template <class T, class CharT = char>
774 struct formatter
775 {
776 using char_type = CharT;
777 };
778
779 template <>
780 struct formatter<etl::monostate>
781 {
782 format_parse_context::iterator parse(format_parse_context& parse_ctx)
783 {
784 return parse_ctx.end();
785 }
786
787 template <class OutputIt>
788 typename format_context<OutputIt>::iterator format(etl::monostate arg, format_context<OutputIt>& fmt_ctx)
789 {
790 (void)arg;
791 return fmt_ctx.out();
792 }
793 };
794
795 namespace private_format
796 {
797 // for 4321, return 1000
798 template <typename UnsignedT, typename = etl::enable_if_t<etl::is_unsigned<UnsignedT>::value>>
799 UnsignedT get_highest_digit(UnsignedT value, size_t base = 10)
800 {
801 ETL_ASSERT(base > 1, ETL_ERROR(bad_format_string_exception));
802 UnsignedT result = 1;
803 value /= base;
804 while (result <= value)
805 {
806 result *= base;
807 }
808 return result;
809 }
810
811 template <typename T>
812 T int_pow(T base, T exp)
813 {
814 T result = 1;
815 while (exp > 0)
816 {
817 if (exp % 2 == 1)
818 result *= base;
819 base *= base;
820 exp /= 2;
821 }
822 return result;
823 }
824
825 template <typename OutputIt, typename T>
826 void format_sign(OutputIt& it, T value, const format_spec_t& spec)
827 {
828 char c = '\0';
829 if (value < 0)
830 {
831 c = '-';
832 }
833 else
834 {
835 switch (spec.sign)
836 {
837 case spec_sign_t::MINUS:
838 // c already set above if negative
839 break;
840 case spec_sign_t::PLUS: c = '+'; break;
841 case spec_sign_t::SPACE: c = ' '; break;
842 default:
843 // invalid sign
844 ETL_ASSERT_FAIL(ETL_ERROR(bad_format_string_exception));
845 }
846 }
847
848 if (c != '\0')
849 {
850 *it = c;
851 ++it;
852 }
853 }
854
855 template <typename OutputIt>
856 void format_sequence(OutputIt& out_it, etl::string_view value)
857 {
858 auto it = value.cbegin();
859 while (it != value.cend())
860 {
861 *out_it = *it;
862 ++it;
863 ++out_it;
864 }
865 }
866
867 template <typename OutputIt, typename T>
868 void format_alternate_form(OutputIt& it, const format_spec_t& spec)
869 {
870 if (spec.hash && spec.type.has_value())
871 {
872 switch (spec.type.value())
873 {
874 case 'b': format_sequence(it, "0b"); break;
875 case 'B': format_sequence(it, "0B"); break;
876 case 'o': format_sequence(it, "0"); break;
877 case 'x': format_sequence(it, "0x"); break;
878 case 'X':
879 format_sequence(it, "0X");
880 break;
881 // default: no prefix
882 }
883 }
884 }
885
886 template <typename OutputIt>
887 void format_plain_char(OutputIt& it, char_type c)
888 {
889 *it = c;
890 ++it;
891 }
892
893 template <typename OutputIt>
894 void format_escaped_char(OutputIt& it, char_type c)
895 {
896 switch (c)
897 {
898 case '\t': format_sequence(it, "\\t"); break;
899 case '\n': format_sequence(it, "\\n"); break;
900 case '\r': format_sequence(it, "\\r"); break;
901 case '"': format_sequence(it, "\\\""); break;
902 case '\'': format_sequence(it, "\\'"); break;
903 case '\\': format_sequence(it, "\\\\"); break;
904 default: *it = c; ++it;
905 }
906 }
907
908 template <typename OutputIt>
909 void fill(OutputIt& it, size_t size, char_type c)
910 {
911 while (size > 0)
912 {
913 *it = c;
914 ++it;
915 --size;
916 }
917 }
918
919 template <size_t default_base = 10>
920 inline size_t base_from_spec(const format_spec_t& spec)
921 {
922 size_t base = default_base;
923 if (spec.type.has_value())
924 {
925 switch (spec.type.value())
926 {
927 case 'a':
928 case 'A': base = 16; break;
929 case 'b':
930 case 'B': base = 2; break;
931 case 'o': base = 8; break;
932 case 'p':
933 case 'P':
934 case 'x':
935 case 'X':
936 base = 16;
937 break;
938 // default: no prefix
939 }
940 }
941 return base;
942 }
943
944 inline bool is_uppercase(const char c)
945 {
946 return c >= 'A' && c <= 'Z';
947 }
948
949 template <typename OutputIt, typename T>
950 void format_digit_char(OutputIt& it, T value, const format_spec_t& spec)
951 {
952 if (value <= 9)
953 {
954 *it = static_cast<char_type>('0' + static_cast<typename etl::make_unsigned<T>::type>(value));
955 }
956 else
957 {
958 if (spec.type.has_value() && is_uppercase(spec.type.value()))
959 {
960 *it = static_cast<char_type>('A' + static_cast<typename etl::make_unsigned<T>::type>(value - 10));
961 }
962 else
963 {
964 *it = static_cast<char_type>('a' + static_cast<typename etl::make_unsigned<T>::type>(value - 10));
965 }
966 }
967 ++it;
968 }
969
970 inline void adjust_width_from_spec(const format_spec_t& spec, size_t& width)
971 {
972 if (spec.zero && spec.width.has_value())
973 {
974 width = etl::max(width, spec.width.value());
975 }
976 }
977
978 inline void check_precision(const format_spec_t& spec)
979 {
980 if (spec.precision.has_value())
981 {
982 // precision not allowed for integer numbers
983 ETL_ASSERT_FAIL(ETL_ERROR(bad_format_string_exception));
984 }
985 }
986
987 // used for both integers and float parts
988 // skip_last_zeros helps in case of printing after-the-decimal zeros which
989 // are redundant then
990 template <typename OutputIt, typename T, T default_base = 10, bool skip_last_zeros = false>
991 void format_plain_num(OutputIt& it, T value, const format_spec_t& spec, size_t width = 0)
992 {
993 using UnsignedT = typename etl::make_unsigned<T>::type;
994
995 UnsignedT unsigned_value = etl::absolute_unsigned(value);
996
997 size_t base = base_from_spec<default_base>(spec);
998 UnsignedT highest_digit = get_highest_digit<UnsignedT>(unsigned_value, base);
999 if (width > 0)
1000 {
1001 UnsignedT align_highest_digit = int_pow<UnsignedT>(base, width - 1);
1002 highest_digit = etl::max<UnsignedT>(align_highest_digit, highest_digit);
1003 }
1004
1005 // this loop is iterated at least once, to print a number
1006 while (highest_digit > 0)
1007 {
1008 UnsignedT digit = unsigned_value / highest_digit;
1009 unsigned_value %= highest_digit;
1010 format_digit_char(it, digit, spec);
1011
1012 if ETL_IF_CONSTEXPR (skip_last_zeros)
1013 {
1014 if (unsigned_value == 0)
1015 {
1016 break;
1017 }
1018 }
1019
1020 highest_digit /= base;
1021 }
1022 }
1023
1024 // for integers
1025 template <typename OutputIt, typename T, bool skip_last_zeros = false>
1026 void format_num(OutputIt& it, T value, const format_spec_t& spec)
1027 {
1028 size_t width = 0;
1029 format_sign<OutputIt, T>(it, value, spec);
1030 format_alternate_form<OutputIt, T>(it, spec);
1031 adjust_width_from_spec(spec, width);
1032 check_precision(spec);
1033 format_plain_num(it, value, spec, width);
1034 }
1035
1036 #if ETL_USING_FORMAT_FLOATING_POINT
1037 template <typename OutputIt, typename T>
1038 void format_floating_default(OutputIt& it, T value, const format_spec_t& spec)
1039 {
1040 const size_t fractional_decimals = 6; // default
1041
1042 T integral;
1043 T fractional = modf(value, &integral);
1044 bool sign;
1045 unsigned long long int fractional_int;
1046 unsigned long long int integral_int;
1047 if (integral < 0.0)
1048 {
1049 sign = true;
1050 fractional_int = static_cast<unsigned long long int>(-fractional * pow(10., fractional_decimals));
1051 integral_int = static_cast<unsigned long long int>(-integral);
1052 }
1053 else
1054 {
1055 sign = false;
1056 fractional_int = static_cast<unsigned long long int>(fractional * pow(10., fractional_decimals));
1057 integral_int = static_cast<unsigned long long int>(integral);
1058 }
1059
1060 private_format::format_sign<OutputIt, int>(it, sign ? -1 : 0, spec);
1061 private_format::format_plain_num<OutputIt, unsigned long long int>(it, integral_int, spec);
1062 private_format::format_sequence<OutputIt>(it, ".");
1063 private_format::format_plain_num<OutputIt, unsigned long long int, 10, true>(it, fractional_int, spec, fractional_decimals);
1064 }
1065
1066 // floating point in hex notation
1067 template <typename OutputIt, typename T>
1068 void format_floating_a(OutputIt& it, T value, const format_spec_t& spec)
1069 {
1070 static const size_t fractional_decimals = 10; // default
1071 static const size_t exponent_decimals = 1;
1072 long long int exponent_int = 0;
1073
1074 bool sign;
1075 unsigned long long int fractional_int;
1076 unsigned long long int integral_int;
1077
1078 T integral;
1079 T fractional = modf(value, &integral);
1080
1081 while (value >= 0x10 || value <= -0x10)
1082 {
1083 ++exponent_int;
1084 value /= 0x10;
1085 fractional = modf(value, &integral);
1086 }
1087
1088 while ((value > 0.0000000000001 && value < 1) || (value < -0.0000000000001 && value > -1))
1089 {
1090 --exponent_int;
1091 value *= 0x10;
1092 fractional = modf(value, &integral);
1093 }
1094
1095 if (integral < 0.0)
1096 {
1097 sign = true;
1098 fractional_int = static_cast<unsigned long long int>(-fractional * pow(static_cast<T>(0x10), fractional_decimals));
1099 integral_int = static_cast<unsigned long long int>(-integral);
1100 }
1101 else
1102 {
1103 sign = false;
1104 fractional_int = static_cast<unsigned long long int>(fractional * pow(static_cast<T>(0x10), fractional_decimals));
1105 integral_int = static_cast<unsigned long long int>(integral);
1106 }
1107
1108 private_format::format_sign<OutputIt, int>(it, sign ? -1 : 0, spec);
1109 private_format::format_plain_char<OutputIt>(it, '0');
1110 char hex_letter = 'x';
1111 if (is_uppercase(spec.type.value()))
1112 {
1113 hex_letter = 'X';
1114 }
1115 private_format::format_plain_char<OutputIt>(it, hex_letter);
1116 private_format::format_plain_num<OutputIt, unsigned long long int, 16>(it, integral_int, spec);
1117 private_format::format_plain_char<OutputIt>(it, '.');
1118 private_format::format_plain_num<OutputIt, unsigned long long int, 16, true>(it, fractional_int, spec, fractional_decimals);
1119 char letter = 'p';
1120 if (is_uppercase(spec.type.value()))
1121 {
1122 letter = 'P';
1123 }
1124 private_format::format_plain_char<OutputIt>(it, letter);
1125 private_format::format_plain_char<OutputIt>(it, (exponent_int < 0) ? '-' : '+');
1126 private_format::format_plain_num<OutputIt, long long int, 16>(it, exponent_int, spec, exponent_decimals);
1127 }
1128
1129 template <typename OutputIt, typename T>
1130 void format_floating_e(OutputIt& it, T value, const format_spec_t& spec)
1131 {
1132 static const size_t fractional_decimals = 6; // default
1133 static const size_t exponent_decimals = 2;
1134 long long int exponent_int = 0;
1135
1136 bool sign;
1137 unsigned long long int fractional_int;
1138 unsigned long long int integral_int;
1139
1140 T integral;
1141 T fractional = modf(value, &integral);
1142
1143 while (value >= 10 || value <= -10)
1144 {
1145 ++exponent_int;
1146 value /= 10;
1147 fractional = modf(value, &integral);
1148 }
1149
1150 while ((value > 0.0000000000001 && value < 1) || (value < -0.0000000000001 && value > -1))
1151 {
1152 --exponent_int;
1153 value *= 10;
1154 fractional = modf(value, &integral);
1155 }
1156
1157 if (integral < 0.0)
1158 {
1159 sign = true;
1160 fractional_int = static_cast<unsigned long long int>(-fractional * pow(10., fractional_decimals));
1161 integral_int = static_cast<unsigned long long int>(-integral);
1162 }
1163 else
1164 {
1165 sign = false;
1166 fractional_int = static_cast<unsigned long long int>(fractional * pow(10., fractional_decimals));
1167 integral_int = static_cast<unsigned long long int>(integral);
1168 }
1169
1170 private_format::format_sign<OutputIt, int>(it, sign ? -1 : 0, spec);
1171 private_format::format_plain_num<OutputIt, unsigned long long int>(it, integral_int, spec);
1172 private_format::format_sequence<OutputIt>(it, ".");
1173 private_format::format_plain_num<OutputIt, unsigned long long int>(it, fractional_int, spec, fractional_decimals);
1174 char letter = 'e';
1175 if (is_uppercase(spec.type.value()))
1176 {
1177 letter = 'E';
1178 }
1179 private_format::format_plain_char<OutputIt>(it, letter);
1180 private_format::format_plain_char<OutputIt>(it, (exponent_int < 0) ? '-' : '+');
1181 private_format::format_plain_num<OutputIt, long long int>(it, exponent_int, spec, exponent_decimals);
1182 }
1183
1184 template <typename OutputIt, typename T>
1185 void format_floating_f(OutputIt& it, T value, const format_spec_t& spec)
1186 {
1187 const size_t fractional_decimals = 6; // default
1188
1189 T integral;
1190 T fractional = modf(value, &integral);
1191 bool sign;
1192 unsigned long long int fractional_int;
1193 unsigned long long int integral_int;
1194 if (integral < 0.0)
1195 {
1196 sign = true;
1197 fractional_int = static_cast<unsigned long long int>(-fractional * pow(10., fractional_decimals));
1198 integral_int = static_cast<unsigned long long int>(-integral);
1199 }
1200 else
1201 {
1202 sign = false;
1203 fractional_int = static_cast<unsigned long long int>(fractional * pow(10., fractional_decimals));
1204 integral_int = static_cast<unsigned long long int>(integral);
1205 }
1206
1207 private_format::format_sign<OutputIt, int>(it, sign ? -1 : 0, spec);
1208 private_format::format_plain_num<OutputIt, unsigned long long int>(it, integral_int, spec);
1209 private_format::format_sequence<OutputIt>(it, ".");
1210 private_format::format_plain_num<OutputIt, unsigned long long int>(it, fractional_int, spec, fractional_decimals);
1211 }
1212 #endif
1213
1214 class dummy_assign_to
1215 {
1216 public:
1217
1218 dummy_assign_to& operator=(char_type)
1219 {
1220 return *this;
1221 }
1222 };
1223
1224 template <class OutputIt>
1225 class limit_assign_to
1226 {
1227 public:
1228
1229 limit_assign_to(OutputIt o, bool is_active)
1230 : out(o)
1231 , active(is_active)
1232 {
1233 }
1234
1235 limit_assign_to& operator=(char_type c)
1236 {
1237 if (active)
1238 {
1239 *out = c;
1240 }
1241 return *this;
1242 }
1243
1244 private:
1245
1246 OutputIt out;
1247 bool active;
1248 };
1249
1250 template <class OutputIt>
1251 class limit_iterator
1252 {
1253 public:
1254
1255 limit_iterator(OutputIt& it, size_t n)
1256 : out(it)
1257 , limit(n)
1258 {
1259 }
1260
1261 limit_iterator(const limit_iterator& other) = default;
1262 limit_iterator(limit_iterator&& other) = default;
1263 limit_iterator& operator=(const limit_iterator& other) = default;
1264 limit_iterator& operator=(limit_iterator&& other) = default;
1265
1266 limit_assign_to<OutputIt> operator*()
1267 {
1268 return limit_assign_to<OutputIt>(out, (limit > 0));
1269 }
1270
1271 limit_iterator& operator++()
1272 {
1273 if (limit > 0)
1274 {
1275 --limit;
1276 ++out;
1277 }
1278 return *this;
1279 }
1280
1281 limit_iterator operator++(int)
1282 {
1283 limit_iterator temp = *this;
1284 if (limit > 0)
1285 {
1286 --limit;
1287 out++;
1288 }
1289 return temp;
1290 }
1291
1292 OutputIt get()
1293 {
1294 return out;
1295 }
1296
1297 private:
1298
1299 OutputIt out;
1300 size_t limit;
1301 };
1302
1303 class counter_iterator
1304 {
1305 public:
1306
1307 counter_iterator()
1308 : count(0)
1309 {
1310 }
1311
1312 counter_iterator(const counter_iterator& other) = default;
1313 counter_iterator& operator=(const counter_iterator& other) = default;
1314
1315 dummy_assign_to operator*()
1316 {
1317 return dummy_assign_to();
1318 }
1319
1320 counter_iterator& operator++()
1321 {
1322 ++count;
1323 return *this;
1324 }
1325
1326 counter_iterator operator++(int)
1327 {
1328 counter_iterator temp = *this;
1329 count++;
1330 return temp;
1331 }
1332
1333 size_t value()
1334 {
1335 return count;
1336 }
1337
1338 private:
1339
1340 size_t count;
1341 };
1342
1343 #if ETL_USING_FORMAT_FLOATING_POINT
1344 template <typename OutputIt, typename T>
1345 void format_floating_g(OutputIt& it, T value, const format_spec_t& spec)
1346 {
1347 private_format::counter_iterator counter_e, counter_f;
1348
1349 format_floating_e(counter_e, value, spec);
1350 format_floating_f(counter_f, value, spec);
1351
1352 if (counter_e.value() < counter_f.value())
1353 {
1354 format_floating_e(it, value, spec);
1355 }
1356 else
1357 {
1358 format_floating_f(it, value, spec);
1359 }
1360 }
1361
1362 template <typename OutputIt, typename T>
1363 void format_floating(OutputIt& it, T value, const format_spec_t& spec)
1364 {
1365 if (isnan(value))
1366 {
1367 if (spec.type.has_value() && (is_uppercase(spec.type.value())))
1368 {
1369 format_sequence(it, "NAN");
1370 }
1371 else
1372 {
1373 format_sequence(it, "nan");
1374 }
1375 }
1376 else if (isinf(value))
1377 {
1378 if (spec.type.has_value() && (is_uppercase(spec.type.value())))
1379 {
1380 format_sequence(it, "INF");
1381 }
1382 else
1383 {
1384 format_sequence(it, "inf");
1385 }
1386 }
1387 else if (!spec.type.has_value())
1388 {
1389 format_floating_default(it, value, spec);
1390 }
1391 else
1392 {
1393 switch (spec.type.value())
1394 {
1395 case 'a':
1396 case 'A': format_floating_a(it, value, spec); break;
1397 case 'e':
1398 case 'E': format_floating_e(it, value, spec); break;
1399 case 'f':
1400 case 'F': format_floating_f(it, value, spec); break;
1401 case 'g':
1402 case 'G': format_floating_g(it, value, spec); break;
1403 default:
1404 // unknown presentation type
1405 ETL_ASSERT_FAIL(ETL_ERROR(bad_format_string_exception));
1406 }
1407 }
1408 }
1409 #endif
1410
1411 template <class OutputIt>
1412 struct format_visitor
1413 {
1414 using output_iterator = OutputIt;
1415
1416 format_visitor(format_parse_context& parse_context, format_context<OutputIt>& f_ctx)
1417 : parse_ctx(parse_context)
1418 , fmt_ctx(f_ctx)
1419 {
1420 }
1421
1422 // for all types in supported_format_types
1423 template <typename T>
1424 void operator()(T value)
1425 {
1426 formatter<T> f;
1427 format_parse_context::iterator it = f.parse(parse_ctx);
1428 parse_ctx.advance_to(it);
1429 OutputIt fit = f.format(value, fmt_ctx);
1430 fmt_ctx.advance_to(fit);
1431 }
1432
1433 format_parse_context& parse_ctx;
1434 format_context<OutputIt>& fmt_ctx;
1435 };
1436
1437 template <class OutputIt>
1438 void output(format_context<OutputIt>& fmt_context, char c)
1439 {
1440 *fmt_context.out() = c;
1441 OutputIt tmp = fmt_context.out();
1442 tmp++;
1443 fmt_context.advance_to(tmp);
1444 }
1445
1446 template <typename OutputIt, typename Int>
1447 typename format_context<OutputIt>::iterator format_aligned_int(Int arg, format_context<OutputIt>& fmt_ctx)
1448 {
1449 size_t prefix_size = 0;
1450 size_t suffix_size = 0;
1451
1452 if (fmt_ctx.format_spec.width)
1453 {
1454 // calculate size
1455 private_format::counter_iterator counter;
1456 private_format::format_num<private_format::counter_iterator, Int>(counter, arg, fmt_ctx.format_spec);
1457
1458 if (counter.value() < fmt_ctx.format_spec.width.value())
1459 {
1460 size_t pad = fmt_ctx.format_spec.width.value() - counter.value();
1461 switch (fmt_ctx.format_spec.align)
1462 {
1463 case private_format::spec_align_t::START:
1464 prefix_size = 0;
1465 suffix_size = pad;
1466 break;
1467 case private_format::spec_align_t::CENTER:
1468 prefix_size = pad / 2;
1469 suffix_size = pad - prefix_size;
1470 break;
1471 case private_format::spec_align_t::NONE: // default
1472 case private_format::spec_align_t::END:
1473 prefix_size = pad;
1474 suffix_size = 0;
1475 break;
1476 default:
1477 // invalid alignment specification
1478 ETL_ASSERT_FAIL(ETL_ERROR(bad_format_string_exception));
1479 }
1480 }
1481 }
1482
1483 // actual output
1484 OutputIt it = fmt_ctx.out();
1485 private_format::fill<OutputIt>(it, prefix_size, fmt_ctx.format_spec.fill);
1486 private_format::format_num<OutputIt, Int>(it, arg, fmt_ctx.format_spec);
1487 private_format::fill<OutputIt>(it, suffix_size, fmt_ctx.format_spec.fill);
1488 return it;
1489 }
1490
1491 #if ETL_USING_FORMAT_FLOATING_POINT
1492 template <typename OutputIt, typename Float>
1493 typename format_context<OutputIt>::iterator format_aligned_floating(Float arg, format_context<OutputIt>& fmt_ctx)
1494 {
1495 size_t prefix_size = 0;
1496 size_t suffix_size = 0;
1497
1498 if (fmt_ctx.format_spec.width)
1499 {
1500 // calculate size
1501 private_format::counter_iterator counter;
1502 private_format::format_floating<private_format::counter_iterator, Float>(counter, arg, fmt_ctx.format_spec);
1503
1504 if (counter.value() < fmt_ctx.format_spec.width.value())
1505 {
1506 size_t pad = fmt_ctx.format_spec.width.value() - counter.value();
1507 switch (fmt_ctx.format_spec.align)
1508 {
1509 case private_format::spec_align_t::START:
1510 prefix_size = 0;
1511 suffix_size = pad;
1512 break;
1513 case private_format::spec_align_t::CENTER:
1514 prefix_size = pad / 2;
1515 suffix_size = pad - prefix_size;
1516 break;
1517 case private_format::spec_align_t::NONE: // default
1518 case private_format::spec_align_t::END:
1519 prefix_size = pad;
1520 suffix_size = 0;
1521 break;
1522 default:
1523 // invalid alignment specification
1524 ETL_ASSERT_FAIL(ETL_ERROR(bad_format_string_exception));
1525 }
1526 }
1527 }
1528
1529 // actual output
1530 OutputIt it = fmt_ctx.out();
1531 private_format::fill<OutputIt>(it, prefix_size, fmt_ctx.format_spec.fill);
1532 private_format::format_floating<OutputIt, Float>(it, arg, fmt_ctx.format_spec);
1533 private_format::fill<OutputIt>(it, suffix_size, fmt_ctx.format_spec.fill);
1534 return it;
1535 }
1536 #endif
1537
1538 template <typename OutputIt>
1539 void format_string_view(OutputIt& it, etl::string_view arg, const format_spec_t& spec)
1540 {
1541 bool escaped = false;
1542 if (spec.type.has_value())
1543 {
1544 switch (spec.type.value())
1545 {
1546 case 's':
1547 // default output
1548 break;
1549 case '?':
1550 // escaped string
1551 escaped = true;
1552 break;
1553 default:
1554 // invalid type for string
1555 ETL_ASSERT_FAIL(ETL_ERROR(bad_format_string_exception));
1556 }
1557 }
1558 size_t limit = etl::numeric_limits<size_t>::max();
1559 if (spec.precision.has_value())
1560 {
1561 limit = spec.precision.value();
1562 }
1563
1564 if (escaped)
1565 {
1566 format_plain_char(it, '"');
1567 }
1568 etl::string_view::const_iterator arg_it = arg.begin();
1569 while (arg_it != arg.cend() && limit > 0)
1570 {
1571 if (escaped)
1572 {
1573 format_escaped_char(it, *arg_it);
1574 }
1575 else
1576 {
1577 format_plain_char(it, *arg_it);
1578 }
1579 ++arg_it;
1580 --limit;
1581 }
1582 if (escaped)
1583 {
1584 format_plain_char(it, '"');
1585 }
1586 }
1587
1588 template <typename OutputIt>
1589 typename format_context<OutputIt>::iterator format_aligned_string_view(etl::string_view arg, format_context<OutputIt>& fmt_ctx)
1590 {
1591 size_t prefix_size = 0;
1592 size_t suffix_size = 0;
1593
1594 if (fmt_ctx.format_spec.width)
1595 {
1596 // calculate size
1597 private_format::counter_iterator counter;
1598 private_format::format_string_view<private_format::counter_iterator>(counter, arg, fmt_ctx.format_spec);
1599
1600 if (counter.value() < fmt_ctx.format_spec.width.value())
1601 {
1602 size_t pad = fmt_ctx.format_spec.width.value() - counter.value();
1603 switch (fmt_ctx.format_spec.align)
1604 {
1605 case private_format::spec_align_t::NONE: // default
1606 case private_format::spec_align_t::START:
1607 prefix_size = 0;
1608 suffix_size = pad;
1609 break;
1610 case private_format::spec_align_t::CENTER:
1611 prefix_size = pad / 2;
1612 suffix_size = pad - prefix_size;
1613 break;
1614 case private_format::spec_align_t::END:
1615 prefix_size = pad;
1616 suffix_size = 0;
1617 break;
1618 default:
1619 // invalid alignment specification
1620 ETL_ASSERT_FAIL(ETL_ERROR(bad_format_string_exception));
1621 }
1622 }
1623 }
1624
1625 // actual output
1626 OutputIt it = fmt_ctx.out();
1627 private_format::fill<OutputIt>(it, prefix_size, fmt_ctx.format_spec.fill);
1628 private_format::format_string_view<OutputIt>(it, arg, fmt_ctx.format_spec);
1629 private_format::fill<OutputIt>(it, suffix_size, fmt_ctx.format_spec.fill);
1630 return it;
1631 }
1632
1633 template <typename OutputIt>
1634 void format_chars(OutputIt& it, const char* arg, const format_spec_t& spec)
1635 {
1636 bool escaped = false;
1637 if (spec.type.has_value())
1638 {
1639 switch (spec.type.value())
1640 {
1641 case 's':
1642 // default output
1643 break;
1644 case '?':
1645 // escaped string
1646 escaped = true;
1647 break;
1648 default:
1649 // invalid type for string
1650 ETL_ASSERT_FAIL(ETL_ERROR(bad_format_string_exception));
1651 }
1652 }
1653 size_t limit = etl::numeric_limits<size_t>::max();
1654 if (spec.precision.has_value())
1655 {
1656 limit = spec.precision.value();
1657 }
1658
1659 if (escaped)
1660 {
1661 format_plain_char(it, '"');
1662 }
1663 const char_type* arg_it = arg;
1664 while (*arg_it != '\0' && limit > 0)
1665 {
1666 if (escaped)
1667 {
1668 format_escaped_char(it, *arg_it);
1669 }
1670 else
1671 {
1672 format_plain_char(it, *arg_it);
1673 }
1674 ++arg_it;
1675 --limit;
1676 }
1677 if (escaped)
1678 {
1679 format_plain_char(it, '"');
1680 }
1681 }
1682
1683 template <typename OutputIt>
1684 typename format_context<OutputIt>::iterator format_aligned_chars(const char* arg, format_context<OutputIt>& fmt_ctx)
1685 {
1686 size_t prefix_size = 0;
1687 size_t suffix_size = 0;
1688
1689 if (fmt_ctx.format_spec.width)
1690 {
1691 // calculate size
1692 private_format::counter_iterator counter;
1693 private_format::format_chars<private_format::counter_iterator>(counter, arg, fmt_ctx.format_spec);
1694
1695 if (counter.value() < fmt_ctx.format_spec.width.value())
1696 {
1697 size_t pad = fmt_ctx.format_spec.width.value() - counter.value();
1698 switch (fmt_ctx.format_spec.align)
1699 {
1700 case private_format::spec_align_t::NONE: // default
1701 case private_format::spec_align_t::START:
1702 prefix_size = 0;
1703 suffix_size = pad;
1704 break;
1705 case private_format::spec_align_t::CENTER:
1706 prefix_size = pad / 2;
1707 suffix_size = pad - prefix_size;
1708 break;
1709 case private_format::spec_align_t::END:
1710 prefix_size = pad;
1711 suffix_size = 0;
1712 break;
1713 default:
1714 // invalid alignment specification
1715 ETL_ASSERT_FAIL(ETL_ERROR(bad_format_string_exception));
1716 }
1717 }
1718 }
1719
1720 // actual output
1721 OutputIt it = fmt_ctx.out();
1722 private_format::fill<OutputIt>(it, prefix_size, fmt_ctx.format_spec.fill);
1723 private_format::format_chars<OutputIt>(it, arg, fmt_ctx.format_spec);
1724 private_format::fill<OutputIt>(it, suffix_size, fmt_ctx.format_spec.fill);
1725 return it;
1726 }
1727
1728 inline void check_char_spec(const format_spec_t& spec)
1729 {
1730 if ((!spec.type.has_value() || spec.type.value() == 'c' || spec.type.value() == '?')
1731 && (spec.sign != spec_sign_t::MINUS || spec.zero || spec.hash || spec.precision))
1732 {
1733 ETL_ASSERT_FAIL(ETL_ERROR(bad_format_string_exception));
1734 }
1735 }
1736
1737 template <typename OutputIt>
1738 void format_char(OutputIt& it, char_type c, const format_spec_t& spec)
1739 {
1740 check_char_spec(spec);
1741 if (spec.type.has_value())
1742 {
1743 switch (spec.type.value())
1744 {
1745 case 'c':
1746 // default output
1747 format_plain_char(it, c);
1748 break;
1749 case '?':
1750 // escaped string
1751 format_plain_char(it, '\'');
1752 format_escaped_char(it, c);
1753 format_plain_char(it, '\'');
1754 break;
1755 case 'b':
1756 case 'B':
1757 case 'd':
1758 case 'o':
1759 case 'x':
1760 case 'X': private_format::format_num<OutputIt, unsigned int>(it, static_cast<unsigned int>(static_cast<unsigned char>(c)), spec); break;
1761 default:
1762 // invalid type for string
1763 ETL_ASSERT_FAIL(ETL_ERROR(bad_format_string_exception));
1764 }
1765 }
1766 else
1767 {
1768 format_plain_char(it, c);
1769 }
1770 }
1771
1772 template <typename OutputIt>
1773 typename format_context<OutputIt>::iterator format_aligned_char(char_type arg, format_context<OutputIt>& fmt_ctx)
1774 {
1775 size_t prefix_size = 0;
1776 size_t suffix_size = 0;
1777
1778 if (fmt_ctx.format_spec.width)
1779 {
1780 // calculate size
1781 private_format::counter_iterator counter;
1782 private_format::format_char<private_format::counter_iterator>(counter, arg, fmt_ctx.format_spec);
1783
1784 if (counter.value() < fmt_ctx.format_spec.width.value())
1785 {
1786 size_t pad = fmt_ctx.format_spec.width.value() - counter.value();
1787 switch (fmt_ctx.format_spec.align)
1788 {
1789 case private_format::spec_align_t::NONE: // default
1790 if (!fmt_ctx.format_spec.type.has_value() || fmt_ctx.format_spec.type.value() == 'c' || fmt_ctx.format_spec.type.value() == '?')
1791 {
1792 prefix_size = 0;
1793 suffix_size = pad;
1794 }
1795 else
1796 {
1797 prefix_size = pad;
1798 suffix_size = 0;
1799 }
1800 break;
1801 case private_format::spec_align_t::START:
1802 prefix_size = 0;
1803 suffix_size = pad;
1804 break;
1805 case private_format::spec_align_t::CENTER:
1806 prefix_size = pad / 2;
1807 suffix_size = pad - prefix_size;
1808 break;
1809 case private_format::spec_align_t::END:
1810 prefix_size = pad;
1811 suffix_size = 0;
1812 break;
1813 default:
1814 // invalid alignment specification
1815 ETL_ASSERT_FAIL(ETL_ERROR(bad_format_string_exception));
1816 }
1817 }
1818 }
1819
1820 // actual output
1821 OutputIt it = fmt_ctx.out();
1822 private_format::fill<OutputIt>(it, prefix_size, fmt_ctx.format_spec.fill);
1823 private_format::format_char<OutputIt>(it, arg, fmt_ctx.format_spec);
1824 private_format::fill<OutputIt>(it, suffix_size, fmt_ctx.format_spec.fill);
1825 return it;
1826 }
1827
1828 template <typename OutputIt>
1829 void format_bool(OutputIt& it, bool value, const format_spec_t& spec)
1830 {
1831 if (spec.type.has_value())
1832 {
1833 switch (spec.type.value())
1834 {
1835 case 's':
1836 // default output
1837 format_sequence(it, value ? "true" : "false");
1838 break;
1839 case 'b':
1840 case 'B':
1841 case 'd':
1842 case 'o':
1843 case 'x':
1844 case 'X': private_format::format_num<OutputIt, unsigned int>(it, static_cast<unsigned int>(static_cast<unsigned char>(value)), spec); break;
1845 default:
1846 // invalid type for string
1847 ETL_ASSERT_FAIL(ETL_ERROR(bad_format_string_exception));
1848 }
1849 }
1850 else
1851 {
1852 format_sequence(it, value ? "true" : "false");
1853 }
1854 }
1855
1856 template <typename OutputIt>
1857 typename format_context<OutputIt>::iterator format_aligned_bool(bool arg, format_context<OutputIt>& fmt_ctx)
1858 {
1859 size_t prefix_size = 0;
1860 size_t suffix_size = 0;
1861
1862 if (fmt_ctx.format_spec.width)
1863 {
1864 // calculate size
1865 private_format::counter_iterator counter;
1866 private_format::format_bool<private_format::counter_iterator>(counter, arg, fmt_ctx.format_spec);
1867
1868 if (counter.value() < fmt_ctx.format_spec.width.value())
1869 {
1870 size_t pad = fmt_ctx.format_spec.width.value() - counter.value();
1871 switch (fmt_ctx.format_spec.align)
1872 {
1873 case private_format::spec_align_t::START:
1874 prefix_size = 0;
1875 suffix_size = pad;
1876 break;
1877 case private_format::spec_align_t::CENTER:
1878 prefix_size = pad / 2;
1879 suffix_size = pad - prefix_size;
1880 break;
1881 case private_format::spec_align_t::NONE: // default
1882 case private_format::spec_align_t::END:
1883 prefix_size = pad;
1884 suffix_size = 0;
1885 break;
1886 default:
1887 // invalid alignment specification
1888 ETL_ASSERT_FAIL(ETL_ERROR(bad_format_string_exception));
1889 }
1890 }
1891 }
1892
1893 // actual output
1894 OutputIt it = fmt_ctx.out();
1895 private_format::fill<OutputIt>(it, prefix_size, fmt_ctx.format_spec.fill);
1896 private_format::format_bool<OutputIt>(it, arg, fmt_ctx.format_spec);
1897 private_format::fill<OutputIt>(it, suffix_size, fmt_ctx.format_spec.fill);
1898 return it;
1899 }
1900
1901 template <typename OutputIt>
1902 void format_pointer(OutputIt& it, const void* value, const format_spec_t& spec)
1903 {
1904 if (spec.type.has_value())
1905 {
1906 switch (spec.type.value())
1907 {
1908 case 'p':
1909 case 'P':
1910 format_sequence(it, spec.type.value() == 'p' ? "0x" : "0X");
1911 format_plain_num<OutputIt, uintptr_t>(it, reinterpret_cast<uintptr_t>(value), spec);
1912 break;
1913 default:
1914 // invalid type for string
1915 ETL_ASSERT_FAIL(ETL_ERROR(bad_format_string_exception));
1916 }
1917 }
1918 else
1919 {
1920 format_sequence(it, "0x");
1921 format_plain_num<OutputIt, uintptr_t>(it, reinterpret_cast<uintptr_t>(value), spec);
1922 }
1923 }
1924
1925 template <typename OutputIt>
1926 typename format_context<OutputIt>::iterator format_aligned_pointer(const void* arg, format_context<OutputIt>& fmt_ctx)
1927 {
1928 size_t prefix_size = 0;
1929 size_t suffix_size = 0;
1930
1931 if (fmt_ctx.format_spec.width)
1932 {
1933 // calculate size
1934 private_format::counter_iterator counter;
1935 private_format::format_pointer<private_format::counter_iterator>(counter, arg, fmt_ctx.format_spec);
1936
1937 if (counter.value() < fmt_ctx.format_spec.width.value())
1938 {
1939 size_t pad = fmt_ctx.format_spec.width.value() - counter.value();
1940 switch (fmt_ctx.format_spec.align)
1941 {
1942 case private_format::spec_align_t::START:
1943 prefix_size = 0;
1944 suffix_size = pad;
1945 break;
1946 case private_format::spec_align_t::CENTER:
1947 prefix_size = pad / 2;
1948 suffix_size = pad - prefix_size;
1949 break;
1950 case private_format::spec_align_t::NONE: // default
1951 case private_format::spec_align_t::END:
1952 prefix_size = pad;
1953 suffix_size = 0;
1954 break;
1955 default:
1956 // invalid alignment specification
1957 ETL_ASSERT_FAIL(ETL_ERROR(bad_format_string_exception));
1958 }
1959 }
1960 }
1961
1962 // actual output
1963 OutputIt it = fmt_ctx.out();
1964 private_format::fill<OutputIt>(it, prefix_size, fmt_ctx.format_spec.fill);
1965 private_format::format_pointer<OutputIt>(it, arg, fmt_ctx.format_spec);
1966 private_format::fill<OutputIt>(it, suffix_size, fmt_ctx.format_spec.fill);
1967 return it;
1968 }
1969 } // namespace private_format
1970
1971 template <>
1972 struct formatter<int>
1973 {
1974 format_parse_context::iterator parse(format_parse_context& parse_ctx)
1975 {
1976 // unified parsing is done already in vformat_to()
1977 return parse_ctx.begin();
1978 }
1979
1980 template <class OutputIt>
1981 typename format_context<OutputIt>::iterator format(int arg, format_context<OutputIt>& fmt_ctx)
1982 {
1983 if (fmt_ctx.format_spec.type.has_value() && fmt_ctx.format_spec.type.value() == 'c')
1984 {
1985 return private_format::format_aligned_char<OutputIt>(static_cast<private_format::char_type>(arg), fmt_ctx);
1986 }
1987 return private_format::format_aligned_int<OutputIt, int>(arg, fmt_ctx);
1988 }
1989 };
1990
1991 template <>
1992 struct formatter<unsigned int>
1993 {
1994 format_parse_context::iterator parse(format_parse_context& parse_ctx)
1995 {
1996 // unified parsing is done already in vformat_to()
1997 return parse_ctx.begin();
1998 }
1999
2000 template <class OutputIt>
2001 typename format_context<OutputIt>::iterator format(unsigned int arg, format_context<OutputIt>& fmt_ctx)
2002 {
2003 if (fmt_ctx.format_spec.type.has_value() && fmt_ctx.format_spec.type.value() == 'c')
2004 {
2005 return private_format::format_aligned_char<OutputIt>(static_cast<private_format::char_type>(arg), fmt_ctx);
2006 }
2007 return private_format::format_aligned_int<OutputIt, unsigned int>(arg, fmt_ctx);
2008 }
2009 };
2010
2011 template <>
2012 struct formatter<long long int>
2013 {
2014 format_parse_context::iterator parse(format_parse_context& parse_ctx)
2015 {
2016 // unified parsing is done already in vformat_to()
2017 return parse_ctx.begin();
2018 }
2019
2020 template <class OutputIt>
2021 typename format_context<OutputIt>::iterator format(long long int arg, format_context<OutputIt>& fmt_ctx)
2022 {
2023 if (fmt_ctx.format_spec.type.has_value() && fmt_ctx.format_spec.type.value() == 'c')
2024 {
2025 return private_format::format_aligned_char<OutputIt>(static_cast<private_format::char_type>(arg), fmt_ctx);
2026 }
2027 return private_format::format_aligned_int<OutputIt, long long int>(arg, fmt_ctx);
2028 }
2029 };
2030
2031 template <>
2032 struct formatter<unsigned long long int>
2033 {
2034 format_parse_context::iterator parse(format_parse_context& parse_ctx)
2035 {
2036 // unified parsing is done already in vformat_to()
2037 return parse_ctx.begin();
2038 }
2039
2040 template <class OutputIt>
2041 typename format_context<OutputIt>::iterator format(unsigned long long int arg, format_context<OutputIt>& fmt_ctx)
2042 {
2043 if (fmt_ctx.format_spec.type.has_value() && fmt_ctx.format_spec.type.value() == 'c')
2044 {
2045 return private_format::format_aligned_char<OutputIt>(static_cast<private_format::char_type>(arg), fmt_ctx);
2046 }
2047 return private_format::format_aligned_int<OutputIt, unsigned long long int>(arg, fmt_ctx);
2048 }
2049 };
2050
2051 template <>
2052 struct formatter<char>
2053 {
2054 format_parse_context::iterator parse(format_parse_context& parse_ctx)
2055 {
2056 // unified parsing is done already in vformat_to()
2057 return parse_ctx.begin();
2058 }
2059
2060 template <class OutputIt>
2061 typename format_context<OutputIt>::iterator format(private_format::char_type arg, format_context<OutputIt>& fmt_ctx)
2062 {
2063 return private_format::format_aligned_char<OutputIt>(arg, fmt_ctx);
2064 }
2065 };
2066
2067 #if ETL_USING_FORMAT_FLOATING_POINT
2068 template <>
2069 struct formatter<float>
2070 {
2071 format_parse_context::iterator parse(format_parse_context& parse_ctx)
2072 {
2073 // unified parsing is done already in vformat_to()
2074 return parse_ctx.begin();
2075 }
2076
2077 template <class OutputIt>
2078 typename format_context<OutputIt>::iterator format(float arg, format_context<OutputIt>& fmt_ctx)
2079 {
2080 return private_format::format_aligned_floating<OutputIt, float>(arg, fmt_ctx);
2081 }
2082 };
2083
2084 template <>
2085 struct formatter<double>
2086 {
2087 format_parse_context::iterator parse(format_parse_context& parse_ctx)
2088 {
2089 // unified parsing is done already in vformat_to()
2090 return parse_ctx.begin();
2091 }
2092
2093 template <class OutputIt>
2094 typename format_context<OutputIt>::iterator format(double arg, format_context<OutputIt>& fmt_ctx)
2095 {
2096 return private_format::format_aligned_floating<OutputIt, double>(arg, fmt_ctx);
2097 }
2098 };
2099
2100 template <>
2101 struct formatter<long double>
2102 {
2103 format_parse_context::iterator parse(format_parse_context& parse_ctx)
2104 {
2105 // unified parsing is done already in vformat_to()
2106 return parse_ctx.begin();
2107 }
2108
2109 template <class OutputIt>
2110 typename format_context<OutputIt>::iterator format(long double arg, format_context<OutputIt>& fmt_ctx)
2111 {
2112 return private_format::format_aligned_floating<OutputIt, long double>(arg, fmt_ctx);
2113 }
2114 };
2115 #endif
2116
2117 template <>
2118 struct formatter<etl::string_view>
2119 {
2120 format_parse_context::iterator parse(format_parse_context& parse_ctx)
2121 {
2122 // unified parsing is done already in vformat_to()
2123 return parse_ctx.begin();
2124 }
2125
2126 template <class OutputIt>
2127 typename format_context<OutputIt>::iterator format(etl::string_view arg, format_context<OutputIt>& fmt_ctx)
2128 {
2129 return private_format::format_aligned_string_view<OutputIt>(arg, fmt_ctx);
2130 }
2131 };
2132
2133 // string formatter
2134 template <>
2135 struct formatter<const char*>
2136 {
2137 format_parse_context::iterator parse(format_parse_context& parse_ctx)
2138 {
2139 // unified parsing is done already in vformat_to()
2140 return parse_ctx.begin();
2141 }
2142
2143 template <class OutputIt>
2144 typename format_context<OutputIt>::iterator format(const char* arg, format_context<OutputIt>& fmt_ctx)
2145 {
2146 return private_format::format_aligned_chars<OutputIt>(arg, fmt_ctx);
2147 }
2148 };
2149
2150 template <>
2151 struct formatter<bool>
2152 {
2153 format_parse_context::iterator parse(format_parse_context& parse_ctx)
2154 {
2155 // unified parsing is done already in vformat_to()
2156 return parse_ctx.begin();
2157 }
2158
2159 template <class OutputIt>
2160 typename format_context<OutputIt>::iterator format(bool arg, format_context<OutputIt>& fmt_ctx)
2161 {
2162 return private_format::format_aligned_bool<OutputIt>(arg, fmt_ctx);
2163 }
2164 };
2165
2166 template <>
2167 struct formatter<const void*>
2168 {
2169 format_parse_context::iterator parse(format_parse_context& parse_ctx)
2170 {
2171 // unified parsing is done already in vformat_to()
2172 return parse_ctx.begin();
2173 }
2174
2175 template <class OutputIt>
2176 typename format_context<OutputIt>::iterator format(const void* arg, format_context<OutputIt>& fmt_ctx)
2177 {
2178 return private_format::format_aligned_pointer<OutputIt>(arg, fmt_ctx);
2179 }
2180 };
2181
2182 template <class OutputIt>
2183 OutputIt vformat_to(OutputIt out, etl::string_view fmt, format_args<OutputIt> args)
2184 {
2185 format_parse_context parse_context(fmt, args.size());
2186 format_context<OutputIt> fmt_context(out, args);
2187 private_format::format_visitor<OutputIt> v(parse_context, fmt_context);
2188
2189 while (parse_context.begin() != parse_context.end())
2190 {
2191 const char c = *parse_context.begin();
2192 private_format::advance(parse_context);
2193 if (c == '{')
2194 {
2195 if (*parse_context.begin() == '{')
2196 {
2197 // escape sequence for literal '{'
2198 private_format::output<OutputIt>(fmt_context, c);
2199 private_format::advance(parse_context);
2200 }
2201 else
2202 {
2203 private_format::parse_format_spec<OutputIt>(parse_context, fmt_context);
2204 etl::optional<size_t> index = fmt_context.format_spec.index;
2205 if (index.has_value())
2206 {
2207 parse_context.check_arg_id(*index);
2208 }
2209 else
2210 {
2211 index = parse_context.next_arg_id();
2212 }
2213 format_arg<OutputIt> arg = args.get(*index);
2214 arg.template visit<void>(v);
2215
2216 ETL_ASSERT(*parse_context.begin() == '}', ETL_ERROR(bad_format_string_exception) /*"Closing brace missing"*/);
2217 if (parse_context.begin() != parse_context.end())
2218 {
2219 private_format::advance(parse_context);
2220 }
2221 }
2222 }
2223 else if (c == '}') // only matches here if } without { is found
2224 {
2225 ETL_ASSERT(*parse_context.begin() == '}', ETL_ERROR(bad_format_string_exception) /*"2nd closing brace missing on escaped closing brace"*/);
2226 // escape sequence for literal '}'
2227 private_format::output<OutputIt>(fmt_context, c);
2228 private_format::advance(parse_context);
2229 }
2230 else
2231 {
2232 private_format::output<OutputIt>(fmt_context, c);
2233 }
2234 }
2235
2236 return fmt_context.out();
2237 }
2238
2239 template <typename OutputIt, typename = etl::enable_if_t< !etl::is_base_of< etl::remove_reference<etl::istring>::type, OutputIt>::value>,
2240 class... Args>
2241 OutputIt format_to(OutputIt out, format_string<Args...> fmt, Args&&... args)
2242 {
2243 auto the_args{make_format_args<OutputIt>(args...)};
2244 return vformat_to(etl::move(out), fmt.get(), format_args<OutputIt>(the_args));
2245 }
2246
2247 template <typename OutputIt, class WrapperIt = private_format::limit_iterator<OutputIt>, class... Args>
2248 OutputIt format_to_n(OutputIt out, size_t n, format_string<Args...> fmt, Args&&... args)
2249 {
2250 auto the_args{make_format_args<WrapperIt>(args...)};
2251 return vformat_to(WrapperIt(out, n), fmt.get(), format_args<WrapperIt>(the_args)).get();
2252 }
2253
2254 // non std in the following, specific to etl
2255 template <class... Args>
2256 etl::istring::iterator format_to(etl::istring& out, format_string<Args...> fmt, Args&&... args)
2257 {
2258 etl::istring::iterator result = format_to_n(out.begin(), out.max_size(), fmt, etl::forward<Args>(args)...);
2259 out.uninitialized_resize(static_cast<size_t>(result - out.begin()));
2260 return result;
2261 }
2262
2263 template <class... Args>
2264 size_t formatted_size(format_string<Args...> fmt, Args&&... args)
2265 {
2266 private_format::counter_iterator it;
2267 it = format_to(it, fmt, etl::forward<Args>(args)...);
2268 return it.value();
2269 }
2270} // namespace etl
2271
2272#endif
2273
2274#endif
ETL_CONSTEXPR14 basic_format_spec & fill(typename TString::value_type c) ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT
Definition basic_format_spec.h:460
ETL_CONSTEXPR14 basic_format_spec & width(uint32_t w) ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT
Definition basic_format_spec.h:379
ETL_CONSTEXPR14 basic_format_spec & precision(uint32_t p) ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT
Definition basic_format_spec.h:406
ETL_CONSTEXPR const_iterator cbegin() const ETL_NOEXCEPT
Returns a const iterator to the beginning of the array.
Definition string_view.h:239
ETL_CONSTEXPR size_t size() const ETL_NOEXCEPT
Returns the size of the array.
Definition string_view.h:307
ETL_CONSTEXPR const_iterator begin() const ETL_NOEXCEPT
Returns a const iterator to the beginning of the array.
Definition string_view.h:231
iterator begin()
Definition basic_string.h:371
void uninitialized_resize(size_type new_size)
Definition basic_string.h:538
ETL_CONSTEXPR20_STL iterator begin() ETL_NOEXCEPT
Returns an iterator to the beginning of the optional.
Definition optional.h:1605
size_type max_size() const
Definition basic_string.h:238
#define ETL_ASSERT(b, e)
Definition error_handler.h:511
etl::monostate monostate
Definition variant_legacy.h:80
bitset_ext
Definition absolute.h:40
ETL_CONSTEXPR TContainer::pointer data(TContainer &container)
Definition iterator.h:1228
void pad(TIString &s, typename TIString::size_type required_size, string_pad_direction pad_direction, typename TIString::value_type pad_char)
pad
Definition string_utilities.h:831
ETL_CONSTEXPR TContainer::size_type size(const TContainer &container)
Definition iterator.h:1192
ETL_CONSTEXPR TContainer::iterator begin(TContainer &container)
Definition iterator.h:967
ETL_CONSTEXPR14 enable_if<!etl::is_specialization< TRep2, etl::chrono::duration >::value, etl::chrono::duration< typenameetl::common_type< TRep1, TRep2 >::type, TPeriod1 > >::type operator*(const etl::chrono::duration< TRep1, TPeriod1 > &lhs, const TRep2 &rhs) ETL_NOEXCEPT
Operator *.
Definition duration.h:541
T & get(array< T, Size > &a)
Definition array.h:1161
ETL_CONSTEXPR TContainer::iterator end(TContainer &container)
Definition iterator.h:997
A 'no-value' placeholder.
Definition monostate.h:42