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 #if ETL_USING_CPP20
80 namespace private_format_check
81 {
82 // Type category for compile-time type/specifier compatibility checking
83 enum class type_category
84 {
85 NONE, // monostate
86 BOOLEAN, // bool
87 CHAR, // char
88 INTEGER, // int, unsigned, long long, unsigned long long, short, etc.
89 FLOAT, // float, double, long double
90 STRING, // const char*, string_view
91 POINTER // const void*
92 };
93
94 // Map a type to its category. Decays and removes cv-qualifiers.
95 template <class T>
96 constexpr type_category get_type_category()
97 {
98 using U = typename etl::remove_cv<typename etl::remove_reference<T>::type>::type;
99
100 // Order matters: bool before integral, char before integral
101 if (etl::is_same<U, bool>::value)
102 return type_category::BOOLEAN;
103 if (etl::is_same<U, char>::value)
104 return type_category::CHAR;
105 if (etl::is_same<U, signed char>::value)
106 return type_category::CHAR;
107 if (etl::is_same<U, unsigned char>::value)
108 return type_category::CHAR;
109 if (etl::is_integral<U>::value)
110 return type_category::INTEGER;
111 if (etl::is_same<U, float>::value)
112 return type_category::FLOAT;
113 if (etl::is_same<U, double>::value)
114 return type_category::FLOAT;
115 if (etl::is_same<U, long double>::value)
116 return type_category::FLOAT;
117 if (etl::is_same<U, const char*>::value)
118 return type_category::STRING;
119 if (etl::is_same<U, char*>::value)
120 return type_category::STRING;
121 if (etl::is_same<U, etl::string_view>::value)
122 return type_category::STRING;
123 if (etl::is_base_of<etl::istring, U>::value)
124 return type_category::STRING;
125 if (etl::is_pointer<U>::value)
126 return type_category::POINTER;
127 if (etl::is_same<U, const void*>::value)
128 return type_category::POINTER;
129 if (etl::is_same<U, void*>::value)
130 return type_category::POINTER;
131 return type_category::NONE; // unknown type: custom formatter, be permissive
132 }
133
134 // Check if a format type character is valid for a given type category.
135 // '\0' means no explicit type was specified (always valid — uses default presentation).
136 inline constexpr bool ct_check_type_spec(type_category cat, char type_char)
137 {
138 if (type_char == '\0')
139 {
140 return true; // no explicit type: always OK, uses default
141 }
142
143 switch (cat)
144 {
145 case type_category::BOOLEAN:
146 // bool: s (as "true"/"false"), b, B, c, d, o, x, X (as integer 0/1)
147 return type_char == 's' || type_char == 'b' || type_char == 'B' || type_char == 'c' || type_char == 'd' || type_char == 'o'
148 || type_char == 'x' || type_char == 'X';
149
150 case type_category::CHAR:
151 // char: c (default), b, B, d, o, x, X (as integer), s, ?
152 return type_char == 'c' || type_char == '?' || type_char == 'b' || type_char == 'B' || type_char == 'd' || type_char == 'o'
153 || type_char == 'x' || type_char == 'X' || type_char == 's';
154
155 case type_category::INTEGER:
156 // integers: b, B, c, d, o, x, X
157 return type_char == 'b' || type_char == 'B' || type_char == 'c' || type_char == 'd' || type_char == 'o' || type_char == 'x'
158 || type_char == 'X';
159
160 case type_category::FLOAT:
161 // floats: a, A, e, E, f, F, g, G
162 return type_char == 'a' || type_char == 'A' || type_char == 'e' || type_char == 'E' || type_char == 'f' || type_char == 'F'
163 || type_char == 'g' || type_char == 'G';
164
165 case type_category::STRING:
166 // strings: s, ?
167 return type_char == 's' || type_char == '?';
168
169 case type_category::POINTER:
170 // pointers: p, P
171 return type_char == 'p' || type_char == 'P';
172
173 case type_category::NONE:
174 default: return true; // unknown/custom type: be permissive, let runtime handle it
175 }
176 }
177
178 inline constexpr bool ct_is_digit(char c)
179 {
180 return c >= '0' && c <= '9';
181 }
182
183 // Parse an unsigned integer from fmt starting at pos. Updates pos past the digits.
184 // Returns the parsed number, or -1 if no digits found, or -2 on overflow.
185 inline constexpr int ct_parse_num(const char* fmt, int& pos)
186 {
187 if (!ct_is_digit(fmt[pos]))
188 {
189 return -1;
190 }
191 int result = 0;
192 while (ct_is_digit(fmt[pos]))
193 {
194 int new_result = result * 10 + (fmt[pos] - '0');
195 if (new_result < result)
196 {
197 // Overflow detected
198 return -2;
199 }
200 result = new_result;
201 ++pos;
202 }
203 return result;
204 }
205
206 inline constexpr bool ct_is_align(char c)
207 {
208 return c == '<' || c == '>' || c == '^';
209 }
210
211 inline constexpr bool ct_is_sign(char c)
212 {
213 return c == '+' || c == '-' || c == ' ';
214 }
215
216 inline constexpr bool ct_is_type(char c)
217 {
218 // All valid type characters from the format spec
219 return (c == 's') || (c == '?') || (c == 'b') || (c == 'B') || (c == 'c') || (c == 'd') || (c == 'o') || (c == 'x') || (c == 'X') || (c == 'a')
220 || (c == 'A') || (c == 'e') || (c == 'E') || (c == 'f') || (c == 'F') || (c == 'g') || (c == 'G') || (c == 'p') || (c == 'P');
221 }
222
223 // Validate a nested replacement field like {}, {0}, {1} inside width/precision.
224 // pos should be at the '{'. Updates pos past the closing '}'.
225 // Updates auto_count / has_manual / has_auto. Returns false on error.
226 inline constexpr bool ct_parse_nested_replacement(const char* fmt, int& pos, int n_args, int& auto_count, bool& has_auto, bool& has_manual)
227 {
228 if (fmt[pos] != '{')
229 return false;
230 ++pos; // skip '{'
231
232 int num = ct_parse_num(fmt, pos);
233 if (num == -2)
234 return false; // overflow
235 if (num >= 0)
236 {
237 // manual index
238 if (has_auto)
239 return false; // mixing
240 has_manual = true;
241 if (num >= n_args)
242 return false;
243 }
244 else
245 {
246 // automatic index
247 if (has_manual)
248 return false; // mixing
249 has_auto = true;
250 if (auto_count >= n_args)
251 return false;
252 ++auto_count;
253 }
254
255 if (fmt[pos] != '}')
256 return false;
257 ++pos; // skip '}'
258 return true;
259 }
260
261 // Skip/validate the format-spec portion after the colon:
262 // [[fill]align][sign][#][0][width][.precision][L][type]
263 // pos is right after ':'. Returns false on invalid spec.
264 // parsed_type is set to the type character found, or '\0' if none.
265 inline constexpr bool ct_skip_format_spec(const char* fmt, int& pos, int n_args, int& auto_count, bool& has_auto, bool& has_manual,
266 char& parsed_type, bool& parsed_has_precision)
267 {
268 parsed_type = '\0';
269 parsed_has_precision = false;
270
271 if (fmt[pos] == '\0' || fmt[pos] == '}')
272 {
273 return true; // empty spec is valid
274 }
275
276 // fill-and-align: either [align] or [fill][align]
277 // Look ahead: if second char is an align char, first is fill
278 if (fmt[pos + 1] != '\0' && ct_is_align(fmt[pos + 1]))
279 {
280 char fill = fmt[pos];
281 if (fill == '{' || fill == '}')
282 return false; // { and } not allowed as fill
283 pos += 2; // skip fill + align
284 }
285 else if (ct_is_align(fmt[pos]))
286 {
287 ++pos; // skip align only
288 }
289
290 // sign
291 if (ct_is_sign(fmt[pos]))
292 {
293 ++pos;
294 }
295
296 // '#'
297 if (fmt[pos] == '#')
298 {
299 ++pos;
300 }
301
302 // '0'
303 if (fmt[pos] == '0')
304 {
305 ++pos;
306 }
307
308 // width: number or nested replacement
309 if (ct_is_digit(fmt[pos]))
310 {
311 if (ct_parse_num(fmt, pos) == -2)
312 return false; // overflow
313 }
314 else if (fmt[pos] == '{')
315 {
316 if (!ct_parse_nested_replacement(fmt, pos, n_args, auto_count, has_auto, has_manual))
317 {
318 return false;
319 }
320 }
321
322 // precision: '.' followed by number or nested replacement
323 bool has_precision = false;
324 if (fmt[pos] == '.')
325 {
326 has_precision = true;
327 ++pos;
328 if (ct_is_digit(fmt[pos]))
329 {
330 if (ct_parse_num(fmt, pos) == -2)
331 return false; // overflow
332 }
333 else if (fmt[pos] == '{')
334 {
335 if (!ct_parse_nested_replacement(fmt, pos, n_args, auto_count, has_auto, has_manual))
336 {
337 return false;
338 }
339 }
340 // else: '.' with no precision number/replacement — still valid (empty precision)
341 }
342
343 // locale-specific: 'L'
344 if (fmt[pos] == 'L')
345 {
346 ++pos;
347 }
348
349 // type
350 if (ct_is_type(fmt[pos]))
351 {
352 parsed_type = fmt[pos];
353 ++pos;
354 }
355
356 // After parsing the spec, we must be at '}' (the closing brace is consumed by the caller)
357 // Any remaining characters before '}' means invalid spec
358 if (fmt[pos] != '}')
359 {
360 return false;
361 }
362
363 parsed_has_precision = has_precision;
364
365 return true;
366 }
367 } // namespace private_format_check
368
369 template <class... Args>
370 constexpr bool check_format(const char* fmt)
371 {
372 const int n_args = static_cast<int>(sizeof...(Args));
373 int pos = 0;
374 int auto_count = 0;
375 bool has_auto = false;
376 bool has_manual = false;
377
378 // Build a constexpr array mapping arg index -> type category
379 const private_format_check::type_category arg_categories[] = {
380 private_format_check::get_type_category<Args>()...,
381 private_format_check::type_category::NONE // sentinel for zero-arg case
382 };
383
384 while (fmt[pos] != '\0')
385 {
386 char c = fmt[pos];
387 ++pos;
388
389 if (c == '{')
390 {
391 if (fmt[pos] == '{')
392 {
393 // escaped '{'
394 ++pos;
395 continue;
396 }
397
398 // Start of a replacement field: [arg_id][:format_spec]
399 int resolved_index = -1;
400 int arg_index = private_format_check::ct_parse_num(fmt, pos);
401 if (arg_index == -2)
402 return false; // overflow in arg index
403 if (arg_index >= 0)
404 {
405 // manual index
406 if (has_auto)
407 return false; // mixing auto and manual
408 has_manual = true;
409 if (arg_index >= n_args)
410 return false; // index out of range
411 resolved_index = arg_index;
412 }
413 else
414 {
415 // automatic index
416 if (has_manual)
417 return false; // mixing auto and manual
418 has_auto = true;
419 if (auto_count >= n_args)
420 return false; // too many arguments
421 resolved_index = auto_count;
422 ++auto_count;
423 }
424
425 char type_char = '\0';
426 bool has_precision = false;
427 if (fmt[pos] == ':')
428 {
429 ++pos; // skip ':'
430 if (!private_format_check::ct_skip_format_spec(fmt, pos, n_args, auto_count, has_auto, has_manual, type_char, has_precision))
431 {
432 return false;
433 }
434 }
435
436 // Validate type specifier against argument type
437 if (resolved_index >= 0 && resolved_index < n_args)
438 {
439 if (!private_format_check::ct_check_type_spec(arg_categories[resolved_index], type_char))
440 {
441 return false;
442 }
443
444 // Precision is not allowed for integer, boolean, or pointer types
445 if (has_precision)
446 {
447 auto cat = arg_categories[resolved_index];
448 if (cat == private_format_check::type_category::INTEGER || cat == private_format_check::type_category::BOOLEAN
449 || cat == private_format_check::type_category::POINTER)
450 {
451 return false;
452 }
453 // Precision is also invalid for char when presented as char (not as integer)
454 if (cat == private_format_check::type_category::CHAR && (type_char == '\0' || type_char == 'c' || type_char == '?'))
455 {
456 return false;
457 }
458 }
459 }
460
461 if (fmt[pos] != '}')
462 {
463 return false; // missing closing brace
464 }
465 ++pos; // skip '}'
466 }
467 else if (c == '}')
468 {
469 if (fmt[pos] != '}')
470 {
471 return false; // unmatched '}'
472 }
473 ++pos; // skip second '}'
474 }
475 }
476
477 return true;
478 }
479
480 inline void please_note_this_is_error_message_format_string_syntax_error() noexcept {}
481 #endif // ETL_USING_CPP20
482
483 template <class... Args>
484 struct basic_format_string
485 {
486 inline ETL_CONSTEVAL basic_format_string(const char* fmt)
487 : _sv(fmt)
488 {
489 #if ETL_USING_CPP20
490 // Compile-time validation: check_format runs at compile time via consteval.
491 // In pre-C++20, runtime checks in vformat_to/parse_format_spec/etc. are sufficient.
492 if (!check_format<Args...>(fmt))
493 {
494 // Calling a non-constexpr function in a consteval context triggers a compile error.
495 please_note_this_is_error_message_format_string_syntax_error();
496 }
497 #endif
498 }
499
500 ETL_CONSTEXPR basic_format_string(const basic_format_string& other) = default;
501 ETL_CONSTEXPR14 basic_format_string& operator=(const basic_format_string& other) = default;
502
503 ETL_CONSTEXPR string_view get() const
504 {
505 return _sv;
506 }
507
508 private:
509
510 string_view _sv;
511 };
512
513 template <class... Args>
514 using format_string = basic_format_string<type_identity_t<Args>...>;
515
516 template <class CharT>
517 class basic_format_parse_context
518 {
519 public:
520
521 using iterator = string_view::const_iterator;
522 using const_iterator = string_view::const_iterator;
523 using char_type = CharT;
524
525 basic_format_parse_context(etl::string_view fmt, size_t n_args = 0)
526 : range(fmt)
527 , num_args(n_args)
528 , current(0)
529 , automatic_mode(false)
530 , manual_mode(false)
531 {
532 }
533
534 basic_format_parse_context<CharT>& operator=(const basic_format_parse_context&) = delete;
535
536 iterator begin() const noexcept
537 {
538 return range.begin();
539 }
540
541 iterator end() const noexcept
542 {
543 return range.end();
544 }
545
546 ETL_CONSTEXPR14 void advance_to(iterator pos)
547 {
548 range = etl::string_view(pos, range.end());
549 }
550
551 ETL_CONSTEXPR14 size_t next_arg_id()
552 {
553 // automatic number generation only allowed if not already in manual mode
554 ETL_ASSERT(manual_mode == false, ETL_ERROR(bad_format_string_exception));
555 automatic_mode = true;
556 ETL_ASSERT(current < num_args, ETL_ERROR(bad_format_string_exception) /* not enough arguments for generated index */);
557 return current++;
558 }
559
560 ETL_CONSTEXPR14 void check_arg_id(size_t id)
561 {
562 // manual index specification only allowed if not already in automatic
563 // mode
564 ETL_ASSERT(automatic_mode == false, ETL_ERROR(bad_format_string_exception));
565 manual_mode = true;
566 ETL_ASSERT(id < num_args, ETL_ERROR(bad_format_string_exception) /* index out of range */);
567 }
568
569 private:
570
571 etl::string_view range;
572 size_t num_args;
573 size_t current;
574 bool automatic_mode;
575 bool manual_mode;
576
577 template <class, class>
578 friend struct formatter;
579 };
580
581 using format_parse_context = basic_format_parse_context<char>;
582
583 // Forward declaration of the formatter primary template (defined later). This
584 // lets basic_format_arg detect user-provided formatter specialisations for
585 // custom types and route them through basic_format_arg::handle.
586 template <class T, class CharT = char>
587 struct formatter;
588
589 // Forward declaration of the format context (defined later) so that
590 // is_formattable can probe for a usable formatter<T>::format() member, which
591 // is templated on the output iterator type.
592 template <class OutputIt, class CharT>
593 class basic_format_context;
594
595 namespace private_format
596 {
597 // Detects whether etl::formatter<T> exposes a usable parse() member.
598 template <typename T, typename = void>
599 struct has_formatter_parse : etl::false_type
600 {
601 };
602
603 template <typename T>
604 struct has_formatter_parse<T, etl::void_t<decltype(etl::declval<etl::formatter<T>&>().parse(etl::declval<format_parse_context&>()))> >
605 : etl::true_type
606 {
607 };
608
609 // Detects whether etl::formatter<T> exposes a usable format() member.
610 // format() is templated on the output iterator, so it is probed with a
611 // representative char* output iterator context.
612 template <typename T, typename = void>
613 struct has_formatter_format : etl::false_type
614 {
615 };
616
617 template <typename T>
618 struct has_formatter_format<T, etl::void_t<decltype(etl::declval<etl::formatter<T>&>().format(
619 etl::declval<const T&>(), etl::declval<etl::basic_format_context<char*, char>&>()))> > : etl::true_type
620 {
621 };
622
623 // The primary formatter template is empty, so a type is only formattable
624 // when a (user-)provided specialisation supplies both a usable parse() and
625 // a usable format() member - mirroring the std::formattable requirements.
626 template <typename T>
627 struct is_formattable : etl::bool_constant<has_formatter_parse<T>::value && has_formatter_format<T>::value>
628 {
629 };
630 } // namespace private_format
631
632 template <class Context>
633 class basic_format_arg
634 {
635 public:
636
637 // Type-erased wrapper that allows user-defined types to be formatted via an
638 // etl::formatter<T> specialisation, mirroring std::basic_format_arg::handle.
639 class handle
640 {
641 public:
642
643 template <typename T>
644 explicit handle(const T& value)
645 : obj(static_cast<const void*>(etl::addressof(value)))
646 , func(&format_custom_type<T>)
647 {
648 }
649
650 void format(etl::basic_format_parse_context<char>& parse_ctx, Context& format_ctx) const
651 {
652 func(parse_ctx, format_ctx, obj);
653 }
654
655 private:
656
657 template <typename T>
658 static void format_custom_type(etl::basic_format_parse_context<char>& parse_ctx, Context& format_ctx, const void* ptr)
659 {
660 const T& value = *static_cast<const T*>(ptr);
661 etl::formatter<T> f;
662 parse_ctx.advance_to(f.parse(parse_ctx));
663 format_ctx.advance_to(f.format(value, format_ctx));
664 }
665
666 const void* obj;
667 typedef void (*function_type)(etl::basic_format_parse_context<char>&, Context&, const void*);
668 function_type func;
669 };
670
671 basic_format_arg() {}
672
673 basic_format_arg(const bool v)
674 : data(v)
675 {
676 }
677
678 basic_format_arg(const int v)
679 : data(v)
680 {
681 }
682
683 basic_format_arg(const short v)
684 : data(static_cast<int>(v))
685 {
686 }
687
688 basic_format_arg(const unsigned short v)
689 : data(static_cast<unsigned int>(v))
690 {
691 }
692
693 basic_format_arg(const long int v)
694 : data(static_cast<long long int>(v))
695 {
696 }
697
698 basic_format_arg(const unsigned int v)
699 : data(v)
700 {
701 }
702
703 basic_format_arg(const long long int v)
704 : data(v)
705 {
706 }
707
708 basic_format_arg(const unsigned long long int v)
709 : data(v)
710 {
711 }
712
713 // Additional type to list of basic types as defined for
714 // std::basic_format_arg: Mapping unsigned long to unsigned long long int
715 basic_format_arg(const unsigned long v)
716 : data(static_cast<unsigned long long int>(v))
717 {
718 }
719
720 basic_format_arg(const char* v)
721 : data(v)
722 {
723 }
724
725 basic_format_arg(char v)
726 : data(v)
727 {
728 }
729
730 basic_format_arg(const signed char v)
731 : data(static_cast<char>(v))
732 {
733 }
734
735 basic_format_arg(const unsigned char v)
736 : data(static_cast<char>(v))
737 {
738 }
739
740 #if ETL_USING_FORMAT_FLOATING_POINT
741 basic_format_arg(const float v)
742 : data(v)
743 {
744 }
745
746 basic_format_arg(const double v)
747 : data(v)
748 {
749 }
750
751 basic_format_arg(const long double v)
752 : data(v)
753 {
754 }
755 #endif
756
757 basic_format_arg(const etl::string_view v)
758 : data(v)
759 {
760 }
761
762 basic_format_arg(const etl::ibasic_string<char>& v)
763 : data(etl::string_view(v.data(), v.size()))
764 {
765 }
766
767 basic_format_arg(const basic_format_arg& other)
768 : data(other.data)
769 {
770 }
771
772 basic_format_arg(const void* v)
773 : data(v)
774 {
775 }
776
777 // Converting constructor for user-defined types that provide an
778 // etl::formatter<T> specialisation. The value is stored type-erased in a
779 // handle, matching the std::basic_format_arg behaviour for custom types.
780 template <typename T, typename = etl::enable_if_t<private_format::is_formattable<T>::value> >
781 basic_format_arg(const T& v)
782 : data(handle(v))
783 {
784 }
785
786 basic_format_arg& operator=(const basic_format_arg& other)
787 {
788 data = other.data;
789 return *this;
790 }
791
792 explicit operator bool() const
793 {
794 return !etl::holds_alternative<etl::monostate>(data);
795 }
796
797 template <class R, class Visitor>
798 R visit(Visitor&& vis)
799 {
800 return etl::visit(etl::forward<Visitor>(vis), data);
801 }
802
803 private:
804
805 // Storage for the argument value.
806 //
807 // This is the limited number of types as defined in std::basic_format_arg
808 // https://en.cppreference.com/w/cpp/utility/format/basic_format_arg.html
809 //
810 // Further types to be supported are mapped onto these via the converting
811 // constructors above. User-defined types are stored type-erased in the
812 // special handle member, which formats them through their etl::formatter
813 // specialisation.
814 etl::variant< etl::monostate, bool, char, int, unsigned int, long long int, unsigned long long int,
815 #if ETL_USING_FORMAT_FLOATING_POINT
816 float, double, long double,
817 #endif
818 const char*, etl::string_view, const void*, handle >
819 data;
820 };
821
822 template <class Context, class... Args>
823 class format_arg_store
824 {
825 public:
826
827 format_arg_store(Args&... args)
828 : _args{args...}
829 {
830 }
831
832 basic_format_arg<Context> get(size_t i) const
833 {
834 return _args.get(i);
835 }
836
837 etl::array_view<basic_format_arg<Context>> get()
838 {
839 return _args;
840 }
841
842 private:
843
844 etl::array<basic_format_arg<Context>, sizeof...(Args)> _args;
845 };
846
847 template <class Context>
848 class basic_format_args
849 {
850 public:
851
852 template <class... Args>
853 basic_format_args(format_arg_store<Context, Args...>& store)
854 : _args(store.get())
855 {
856 }
857
858 basic_format_args(const basic_format_args<Context>& other)
859 : _args(other._args)
860 {
861 }
862
863 basic_format_args& operator=(const basic_format_args<Context>& other)
864 {
865 _args = other._args;
866 return *this;
867 }
868
869 basic_format_arg<Context> get(size_t i) const
870 {
871 return _args[i];
872 }
873
874 // non-standard
875 size_t size()
876 {
877 return _args.size();
878 }
879
880 private:
881
882 etl::array_view<basic_format_arg<Context>> _args;
883 };
884
885 namespace private_format
886 {
887 using char_type = char;
888
889 enum class spec_align_t
890 {
891 NONE, // default
892 START,
893 END,
894 CENTER
895 };
896
897 enum class spec_sign_t
898 {
899 MINUS, // default
900 PLUS,
901 SPACE
902 };
903
904 struct format_spec_t
905 {
906 etl::optional<size_t> index{etl::nullopt_t()};
907 spec_align_t align{spec_align_t::NONE}; // '<' / '>' / '^' / none (default)
908 char_type fill{' '}; // fill character (' ' is default)
909 spec_sign_t sign{spec_sign_t::MINUS}; // '+' / '-' (default) / ' '
910 bool hash{false}; // #
911 bool zero{false}; // 0
912 etl::optional<size_t> width{etl::nullopt_t()}; // the arg index if width_nested_replacement == true
913 bool width_nested_replacement{false}; // {}
914 etl::optional<size_t> precision{etl::nullopt_t()}; // the arg index if
915 // precision_nested_replacement == true
916 bool precision_nested_replacement{false}; // {}
917 bool locale_specific{false}; // 'L'
918 etl::optional<char> type{etl::nullopt_t()}; // literal 's', 'b', 'd', ...
919 };
920 } // namespace private_format
921
922 template <class OutputIt, class CharT>
923 class basic_format_context
924 {
925 public:
926
927 using iterator = OutputIt;
928 using char_type = CharT;
929
930 basic_format_context(const basic_format_context& other)
931 : _it(other._it)
932 , _format_args(other._format_args)
933 {
934 }
935
936 basic_format_context(OutputIt it, basic_format_args<basic_format_context>& fmt_args)
937 : _it(it)
938 , _format_args(fmt_args)
939 {
940 }
941
942 basic_format_context& operator=(const basic_format_context&) = delete;
943
944 basic_format_arg<basic_format_context> arg(size_t id) const
945 {
946 return _format_args.get(id);
947 }
948
949 iterator out()
950 {
951 return _it;
952 }
953
954 void advance_to(iterator it)
955 {
956 _it = it;
957 }
958
959 private_format::format_spec_t format_spec;
960
961 private:
962
963 iterator _it;
964 basic_format_args<basic_format_context>& _format_args;
965 };
966
967 template <class OutputIt>
968 using format_context = basic_format_context<OutputIt, char>;
969
970 template <class OutputIt>
971 using format_args = basic_format_args<format_context<OutputIt>>;
972
973 template <class OutputIt>
974 using format_arg = basic_format_arg<format_context<OutputIt>>;
975
976 template <class OutputIt, class Context = format_context<OutputIt>, class... Args>
977 format_arg_store<Context, Args...> make_format_args(Args&... args)
978 {
979 return format_arg_store<Context, Args...>(args...);
980 }
981
982 namespace private_format
983 {
984 inline bool is_digit(const char c)
985 {
986 return c >= '0' && c <= '9';
987 }
988
989 inline void advance(format_parse_context& parse_ctx)
990 {
991 parse_ctx.advance_to(parse_ctx.begin() + 1);
992 }
993
994 inline etl::optional<size_t> parse_num(format_parse_context& parse_ctx)
995 {
996 etl::optional<size_t> result;
997 auto fmt_it = parse_ctx.begin();
998 while (fmt_it != parse_ctx.end())
999 {
1000 const char c = *fmt_it;
1001 if (is_digit(c))
1002 {
1003 size_t old_value = result.value_or(0);
1004 size_t new_value = old_value * 10 + static_cast<size_t>(c - '0');
1005 if (new_value < old_value)
1006 {
1007 // Overflow detected
1008 ETL_ASSERT_FAIL(ETL_ERROR(bad_format_string_exception));
1009 }
1010 result = new_value;
1011 }
1012 else
1013 {
1014 break;
1015 }
1016 ++fmt_it;
1017 }
1018 if (result.has_value())
1019 {
1020 parse_ctx.advance_to(fmt_it);
1021 }
1022 return result;
1023 }
1024
1025 inline etl::optional<char> parse_any_of(format_parse_context& parse_ctx, etl::string_view chars)
1026 {
1027 etl::optional<char> result;
1028 auto fmt_it = parse_ctx.begin();
1029 if (fmt_it != parse_ctx.end())
1030 {
1031 const char c = *fmt_it;
1032 auto it = etl::find(chars.cbegin(), chars.cend(), c);
1033 if (it != chars.cend())
1034 {
1035 result = *it;
1036 ++fmt_it;
1037 parse_ctx.advance_to(fmt_it);
1038 }
1039 }
1040 return result;
1041 }
1042
1043 inline bool parse_char(format_parse_context& parse_ctx, char c)
1044 {
1045 auto fmt_it = parse_ctx.begin();
1046 if (fmt_it != parse_ctx.end())
1047 {
1048 char value = *fmt_it;
1049 if (value == c)
1050 {
1051 ++fmt_it;
1052 parse_ctx.advance_to(fmt_it);
1053 return true;
1054 }
1055 }
1056 return false;
1057 }
1058
1059 inline bool is_align_character(char c)
1060 {
1061 return c == '<' || c == '>' || c == '^';
1062 }
1063
1064 inline spec_align_t align_from_char(char c)
1065 {
1066 spec_align_t result = spec_align_t::NONE;
1067 switch (c)
1068 {
1069 case '<': result = spec_align_t::START; break;
1070 case '>': result = spec_align_t::END; break;
1071 case '^': result = spec_align_t::CENTER; break;
1072 default:
1073 // invalid alignment specification
1074 ETL_ASSERT_FAIL(ETL_ERROR(bad_format_string_exception));
1075 }
1076 return result;
1077 }
1078
1079 inline spec_align_t parse_fill_and_align(format_parse_context& parse_ctx, char_type& fill)
1080 {
1081 spec_align_t result = spec_align_t::NONE;
1082 fill = ' '; // default
1083
1084 auto fmt_it = parse_ctx.begin();
1085 if (fmt_it != parse_ctx.end())
1086 {
1087 const char c = *fmt_it;
1088 ++fmt_it;
1089
1090 if (is_align_character(c))
1091 {
1092 result = align_from_char(c);
1093 parse_ctx.advance_to(fmt_it);
1094 }
1095 else if (fmt_it != parse_ctx.end())
1096 {
1097 const char c2 = *fmt_it;
1098 ++fmt_it;
1099 if (is_align_character(c2))
1100 {
1101 result = align_from_char(c2);
1102 ETL_ASSERT(c != '{' && c != '}',
1103 ETL_ERROR(bad_format_string_exception)); // no { or } allowed as
1104 // fill character
1105 fill = c;
1106 parse_ctx.advance_to(fmt_it);
1107 }
1108 }
1109 else
1110 {
1111 // no align and fill spec (valid)
1112 }
1113 }
1114 return result;
1115 }
1116
1117 inline spec_sign_t sign_from_char(const char c)
1118 {
1119 spec_sign_t result = spec_sign_t::MINUS;
1120 switch (c)
1121 {
1122 case '-': result = spec_sign_t::MINUS; break;
1123 case '+': result = spec_sign_t::PLUS; break;
1124 case ' ': result = spec_sign_t::SPACE; break;
1125 default:
1126 // invalid sign character c
1127 ETL_ASSERT_FAIL(ETL_ERROR(bad_format_string_exception));
1128 }
1129 return result;
1130 }
1131
1132 inline bool parse_nested_replacement(format_parse_context& parse_ctx, etl::optional<size_t>& index)
1133 {
1134 bool start = parse_char(parse_ctx, '{');
1135 if (start)
1136 {
1137 auto num = parse_num(parse_ctx);
1138 if (num)
1139 {
1140 // manual mode
1141 index = num;
1142 parse_ctx.check_arg_id(*index);
1143 bool end = parse_char(parse_ctx, '}');
1144 if (end)
1145 {
1146 return true;
1147 }
1148 else
1149 {
1150 // bad nested replacement index spec
1151 ETL_ASSERT_FAIL(ETL_ERROR(bad_format_string_exception));
1152 }
1153 }
1154 else
1155 {
1156 bool end = parse_char(parse_ctx, '}');
1157 if (end)
1158 {
1159 index = parse_ctx.next_arg_id();
1160 return true;
1161 }
1162 else
1163 {
1164 // bad nested replacement spec
1165 ETL_ASSERT_FAIL(ETL_ERROR(bad_format_string_exception));
1166 }
1167 }
1168 }
1169 return false;
1170 }
1171
1172 template <class OutputIt>
1173 void parse_format_spec(format_parse_context& parse_ctx, format_context<OutputIt>& fmt_context)
1174 {
1175 auto& format_spec = fmt_context.format_spec;
1176
1177 format_spec = format_spec_t(); // reset format_spec to defaults
1178
1179 format_spec.index = parse_num(parse_ctx); // optional explicit index
1180
1181 // Consume the value's auto-index before parsing the format spec body,
1182 // so that nested replacement fields for width/precision get correct
1183 // auto-indices. Per C++ standard, in {:{}}, the value arg is consumed
1184 // first (arg 0), then the width arg (arg 1).
1185 if (!format_spec.index.has_value())
1186 {
1187 format_spec.index = parse_ctx.next_arg_id();
1188 }
1189 else
1190 {
1191 parse_ctx.check_arg_id(*format_spec.index);
1192 }
1193
1194 bool colon = parse_char(parse_ctx, ':');
1195 if (colon)
1196 {
1197 format_spec.align = parse_fill_and_align(parse_ctx, format_spec.fill);
1198
1199 etl::optional<char> sign = parse_any_of(parse_ctx, "+- ");
1200 if (sign)
1201 {
1202 format_spec.sign = sign_from_char(*sign);
1203 }
1204
1205 format_spec.hash = parse_char(parse_ctx, '#');
1206 format_spec.zero = parse_char(parse_ctx, '0');
1207
1208 format_spec.width = parse_num(parse_ctx);
1209 if (!format_spec.width)
1210 {
1211 // possibly with via nested replacement
1212 format_spec.width_nested_replacement = parse_nested_replacement(parse_ctx, format_spec.width);
1213 }
1214
1215 if (parse_char(parse_ctx, '.'))
1216 {
1217 format_spec.precision = parse_num(parse_ctx);
1218 if (!format_spec.precision)
1219 {
1220 // possibly with via nested replacement
1221 format_spec.precision_nested_replacement = parse_nested_replacement(parse_ctx, format_spec.precision);
1222 }
1223 }
1224
1225 format_spec.locale_specific = parse_char(parse_ctx, 'L');
1226
1227 format_spec.type = parse_any_of(parse_ctx, "s?bBcdoxXaAeEfFgGpP");
1228 }
1229 }
1230 } // namespace private_format
1231
1232 template <class T, class CharT>
1233 struct formatter
1234 {
1235 using char_type = CharT;
1236 };
1237
1238 template <>
1239 struct formatter<etl::monostate>
1240 {
1241 format_parse_context::iterator parse(format_parse_context& parse_ctx)
1242 {
1243 return parse_ctx.end();
1244 }
1245
1246 template <class OutputIt>
1247 typename format_context<OutputIt>::iterator format(etl::monostate arg, format_context<OutputIt>& fmt_ctx)
1248 {
1249 (void)arg;
1250 return fmt_ctx.out();
1251 }
1252 };
1253
1254 namespace private_format
1255 {
1256 // for 4321, return 1000
1257 template <typename UnsignedT, typename = etl::enable_if_t<etl::is_unsigned<UnsignedT>::value>>
1258 UnsignedT get_highest_digit(UnsignedT value, size_t base = 10)
1259 {
1260 ETL_ASSERT(base > 1, ETL_ERROR(bad_format_string_exception));
1261 UnsignedT result = 1;
1262 value /= base;
1263 while (result <= value)
1264 {
1265 result *= base;
1266 }
1267 return result;
1268 }
1269
1270 template <typename T>
1271 T int_pow(T base, T exp)
1272 {
1273 T result = 1;
1274 while (exp > 0)
1275 {
1276 if (exp % 2 == 1)
1277 result *= base;
1278 base *= base;
1279 exp /= 2;
1280 }
1281 return result;
1282 }
1283
1284 template <typename OutputIt, typename T>
1285 void format_sign(OutputIt& it, T value, const format_spec_t& spec)
1286 {
1287 char c = '\0';
1288 if (value < 0)
1289 {
1290 c = '-';
1291 }
1292 else
1293 {
1294 switch (spec.sign)
1295 {
1296 case spec_sign_t::MINUS:
1297 // c already set above if negative
1298 break;
1299 case spec_sign_t::PLUS: c = '+'; break;
1300 case spec_sign_t::SPACE: c = ' '; break;
1301 default:
1302 // invalid sign
1303 ETL_ASSERT_FAIL(ETL_ERROR(bad_format_string_exception));
1304 }
1305 }
1306
1307 if (c != '\0')
1308 {
1309 *it = c;
1310 ++it;
1311 }
1312 }
1313
1314 template <typename OutputIt>
1315 void format_sequence(OutputIt& out_it, etl::string_view value)
1316 {
1317 auto it = value.cbegin();
1318 while (it != value.cend())
1319 {
1320 *out_it = *it;
1321 ++it;
1322 ++out_it;
1323 }
1324 }
1325
1326 template <typename OutputIt, typename T>
1327 void format_alternate_form(OutputIt& it, T value, const format_spec_t& spec)
1328 {
1329 if (spec.hash && spec.type.has_value())
1330 {
1331 switch (spec.type.value())
1332 {
1333 case 'b': format_sequence(it, "0b"); break;
1334 case 'B': format_sequence(it, "0B"); break;
1335 case 'o':
1336 // Per C++ standard, # for octal adds leading 0 only if not already present
1337 if (value != 0)
1338 {
1339 format_sequence(it, "0");
1340 }
1341 break;
1342 case 'x': format_sequence(it, "0x"); break;
1343 case 'X':
1344 format_sequence(it, "0X");
1345 break;
1346 // default: no prefix
1347 }
1348 }
1349 }
1350
1351 template <typename OutputIt>
1352 void format_plain_char(OutputIt& it, char_type c)
1353 {
1354 *it = c;
1355 ++it;
1356 }
1357
1358 template <typename OutputIt>
1359 void format_escaped_char(OutputIt& it, char_type c)
1360 {
1361 switch (c)
1362 {
1363 case '\t': format_sequence(it, "\\t"); break;
1364 case '\n': format_sequence(it, "\\n"); break;
1365 case '\r': format_sequence(it, "\\r"); break;
1366 case '"': format_sequence(it, "\\\""); break;
1367 case '\'': format_sequence(it, "\\'"); break;
1368 case '\\': format_sequence(it, "\\\\"); break;
1369 default: *it = c; ++it;
1370 }
1371 }
1372
1373 template <typename OutputIt>
1374 void fill(OutputIt& it, size_t size, char_type c)
1375 {
1376 while (size > 0)
1377 {
1378 *it = c;
1379 ++it;
1380 --size;
1381 }
1382 }
1383
1384 template <size_t default_base = 10>
1385 inline size_t base_from_spec(const format_spec_t& spec)
1386 {
1387 size_t base = default_base;
1388 if (spec.type.has_value())
1389 {
1390 switch (spec.type.value())
1391 {
1392 case 'a':
1393 case 'A': base = 16; break;
1394 case 'b':
1395 case 'B': base = 2; break;
1396 case 'o': base = 8; break;
1397 case 'p':
1398 case 'P':
1399 case 'x':
1400 case 'X':
1401 base = 16;
1402 break;
1403 // default: no prefix
1404 }
1405 }
1406 return base;
1407 }
1408
1409 inline bool is_uppercase(const char c)
1410 {
1411 return c >= 'A' && c <= 'Z';
1412 }
1413
1414 template <typename OutputIt, typename T>
1415 void format_digit_char(OutputIt& it, T value, const format_spec_t& spec)
1416 {
1417 if (value <= 9)
1418 {
1419 *it = static_cast<char_type>('0' + static_cast<typename etl::make_unsigned<T>::type>(value));
1420 }
1421 else
1422 {
1423 if (spec.type.has_value() && is_uppercase(spec.type.value()))
1424 {
1425 *it = static_cast<char_type>('A' + static_cast<typename etl::make_unsigned<T>::type>(value - 10));
1426 }
1427 else
1428 {
1429 *it = static_cast<char_type>('a' + static_cast<typename etl::make_unsigned<T>::type>(value - 10));
1430 }
1431 }
1432 ++it;
1433 }
1434
1435 inline void adjust_width_from_spec(const format_spec_t& spec, size_t& width)
1436 {
1437 if (spec.zero && spec.width.has_value())
1438 {
1439 width = etl::max(width, spec.width.value());
1440 }
1441 }
1442
1443 inline void check_precision(const format_spec_t& spec)
1444 {
1445 if (spec.precision.has_value())
1446 {
1447 // precision not allowed for integer numbers
1448 ETL_ASSERT_FAIL(ETL_ERROR(bad_format_string_exception));
1449 }
1450 }
1451
1452 // used for both integers and float parts
1453 // skip_last_zeros helps in case of printing after-the-decimal zeros which
1454 // are redundant then
1455 template <typename OutputIt, typename T, T default_base = 10, bool skip_last_zeros = false>
1456 void format_plain_num(OutputIt& it, T value, const format_spec_t& spec, size_t width = 0)
1457 {
1458 using UnsignedT = typename etl::make_unsigned<T>::type;
1459
1460 UnsignedT unsigned_value = etl::absolute_unsigned(value);
1461
1462 size_t base = base_from_spec<default_base>(spec);
1463 UnsignedT highest_digit = get_highest_digit<UnsignedT>(unsigned_value, base);
1464 if (width > 0)
1465 {
1466 UnsignedT align_highest_digit = int_pow<UnsignedT>(base, width - 1);
1467 highest_digit = etl::max<UnsignedT>(align_highest_digit, highest_digit);
1468 }
1469
1470 // this loop is iterated at least once, to print a number
1471 while (highest_digit > 0)
1472 {
1473 UnsignedT digit = unsigned_value / highest_digit;
1474 unsigned_value %= highest_digit;
1475 format_digit_char(it, digit, spec);
1476
1477 if ETL_IF_CONSTEXPR (skip_last_zeros)
1478 {
1479 if (unsigned_value == 0)
1480 {
1481 break;
1482 }
1483 }
1484
1485 highest_digit /= base;
1486 }
1487 }
1488
1489 // for integers
1490 template <typename OutputIt, typename T, bool skip_last_zeros = false>
1491 void format_num(OutputIt& it, T value, const format_spec_t& spec)
1492 {
1493 size_t width = 0;
1494 format_sign<OutputIt, T>(it, value, spec);
1495 format_alternate_form<OutputIt, T>(it, value, spec);
1496 adjust_width_from_spec(spec, width);
1497 check_precision(spec);
1498 format_plain_num(it, value, spec, width);
1499 }
1500
1501 #if ETL_USING_FORMAT_FLOATING_POINT
1502 #if ETL_NOT_USING_FORMAT_LONG_DOUBLE_MATH
1503 //***********************************
1504 // Math function wrappers to handle toolchains that don't provide
1505 // long double math functions (log10l, floorl, powl, modfl, roundl).
1506 // When ETL_FORMAT_NO_LONG_DOUBLE_MATH is defined, long double overloads
1507 // cast through double. For float and double, the standard functions are
1508 // called directly via the template versions.
1509 //***********************************
1510 inline long double format_log10(long double value)
1511 {
1512 return static_cast<long double>(::log10(static_cast<double>(value)));
1513 }
1514 inline long double format_floor(long double value)
1515 {
1516 return static_cast<long double>(::floor(static_cast<double>(value)));
1517 }
1518 inline long double format_pow(long double base, long double exp)
1519 {
1520 return static_cast<long double>(::pow(static_cast<double>(base), static_cast<double>(exp)));
1521 }
1522 inline long double format_round(long double value)
1523 {
1524 return static_cast<long double>(::round(static_cast<double>(value)));
1525 }
1526 inline long double format_modf(long double value, long double* iptr)
1527 {
1528 double d_iptr;
1529 double result = ::modf(static_cast<double>(value), &d_iptr);
1530 *iptr = static_cast<long double>(d_iptr);
1531 return static_cast<long double>(result);
1532 }
1533 #endif
1534
1535 template <typename T>
1536 T format_log10(T value)
1537 {
1538 return ::log10(value);
1539 }
1540 template <typename T>
1541 T format_floor(T value)
1542 {
1543 return ::floor(value);
1544 }
1545 template <typename T>
1546 T format_pow(T base, T exp)
1547 {
1548 return ::pow(base, exp);
1549 }
1550 template <typename T>
1551 T format_round(T value)
1552 {
1553 return ::round(value);
1554 }
1555 template <typename T>
1556 T format_modf(T value, T* iptr)
1557 {
1558 return ::modf(value, iptr);
1559 }
1560
1561 template <typename OutputIt, typename T>
1562 void format_floating_default(OutputIt& it, T value, const format_spec_t& spec)
1563 {
1564 const size_t fractional_decimals = 6; // default
1565
1566 // Detect sign using signbit to correctly handle -0.0
1567 bool sign = signbit(value);
1568 T abs_value = sign ? -value : value;
1569
1570 // Use scientific notation for values that would overflow unsigned long long
1571 // or that are too small for meaningful fixed-point digits
1572 if (abs_value >= static_cast<T>(1e18) || (abs_value > static_cast<T>(0) && abs_value < static_cast<T>(1e-6)))
1573 {
1574 format_spec_t spec_e = spec;
1575 spec_e.type = 'e';
1576 format_floating_e(it, value, spec_e);
1577 return;
1578 }
1579
1580 T integral;
1581 T fractional = format_modf(value, &integral);
1582
1583 // Take absolute values to avoid casting negative values to unsigned
1584 if (sign)
1585 {
1586 fractional = -fractional;
1587 integral = -integral;
1588 }
1589
1590 unsigned long long int scale = int_pow<unsigned long long int>(10, fractional_decimals);
1591 unsigned long long int fractional_int = static_cast<unsigned long long int>(format_round(fractional * scale));
1592 unsigned long long int integral_int = static_cast<unsigned long long int>(integral);
1593
1594 if (fractional_int == scale)
1595 {
1596 fractional_int = 0;
1597 ++integral_int;
1598 }
1599
1600 private_format::format_sign<OutputIt, int>(it, sign ? -1 : 0, spec);
1601 private_format::format_plain_num<OutputIt, unsigned long long int>(it, integral_int, spec);
1602 private_format::format_sequence<OutputIt>(it, ".");
1603 private_format::format_plain_num<OutputIt, unsigned long long int, 10, true>(it, fractional_int, spec, fractional_decimals);
1604 }
1605
1606 // floating point in hex notation
1607 template <typename OutputIt, typename T>
1608 void format_floating_a(OutputIt& it, T value, const format_spec_t& spec)
1609 {
1610 static const size_t fractional_decimals = 10; // default
1611 static const size_t exponent_decimals = 1;
1612 long long int exponent_int = 0;
1613
1614 // Detect sign using signbit to correctly handle -0.0
1615 bool sign = signbit(value);
1616
1617 T integral;
1618 T fractional = format_modf(value, &integral);
1619
1620 while (value >= 0x10 || value <= -0x10)
1621 {
1622 ++exponent_int;
1623 value /= 0x10;
1624 fractional = format_modf(value, &integral);
1625 }
1626
1627 while ((value > 0.0000000000001 && value < 1) || (value < -0.0000000000001 && value > -1))
1628 {
1629 --exponent_int;
1630 value *= 0x10;
1631 fractional = format_modf(value, &integral);
1632 }
1633
1634 // Take absolute values to avoid casting negative values to unsigned
1635 if (sign)
1636 {
1637 fractional = -fractional;
1638 integral = -integral;
1639 }
1640
1641 unsigned long long int scale = int_pow<unsigned long long int>(0x10, fractional_decimals);
1642 unsigned long long int fractional_int = static_cast<unsigned long long int>(format_round(fractional * scale));
1643 unsigned long long int integral_int = static_cast<unsigned long long int>(integral);
1644
1645 if (fractional_int == scale)
1646 {
1647 fractional_int = 0;
1648 ++integral_int;
1649 }
1650
1651 private_format::format_sign<OutputIt, int>(it, sign ? -1 : 0, spec);
1652 private_format::format_plain_char<OutputIt>(it, '0');
1653 char hex_letter = 'x';
1654 if (is_uppercase(spec.type.value()))
1655 {
1656 hex_letter = 'X';
1657 }
1658 private_format::format_plain_char<OutputIt>(it, hex_letter);
1659 private_format::format_plain_num<OutputIt, unsigned long long int, 16>(it, integral_int, spec);
1660 private_format::format_plain_char<OutputIt>(it, '.');
1661 private_format::format_plain_num<OutputIt, unsigned long long int, 16, true>(it, fractional_int, spec, fractional_decimals);
1662 char letter = 'p';
1663 if (is_uppercase(spec.type.value()))
1664 {
1665 letter = 'P';
1666 }
1667 private_format::format_plain_char<OutputIt>(it, letter);
1668 private_format::format_plain_char<OutputIt>(it, (exponent_int < 0) ? '-' : '+');
1669 private_format::format_plain_num<OutputIt, long long int, 16>(it, exponent_int, spec, exponent_decimals);
1670 }
1671
1672 template <typename OutputIt, typename T>
1673 void format_floating_e(OutputIt& it, T value, const format_spec_t& spec)
1674 {
1675 static const size_t fractional_decimals = 6; // default
1676 static const size_t exponent_decimals = 2;
1677 long long int exponent_int = 0;
1678
1679 // Detect sign using signbit to correctly handle -0.0
1680 bool sign = signbit(value);
1681
1682 T abs_value = sign ? -value : value;
1683
1684 if (abs_value > static_cast<T>(0))
1685 {
1686 exponent_int = static_cast<long long int>(format_floor(format_log10(abs_value)));
1687 value = abs_value / format_pow(static_cast<T>(10), static_cast<T>(exponent_int));
1688 // Correct for floating-point rounding in log10/pow
1689 if (value >= static_cast<T>(10))
1690 {
1691 value /= static_cast<T>(10);
1692 ++exponent_int;
1693 }
1694 else if (value < static_cast<T>(1))
1695 {
1696 value *= static_cast<T>(10);
1697 --exponent_int;
1698 }
1699 }
1700 else
1701 {
1702 value = static_cast<T>(0);
1703 }
1704
1705 T integral;
1706 T fractional = format_modf(value, &integral);
1707
1708 unsigned long long int scale = int_pow<unsigned long long int>(10, fractional_decimals);
1709 unsigned long long int fractional_int = static_cast<unsigned long long int>(format_round(fractional * scale));
1710 unsigned long long int integral_int = static_cast<unsigned long long int>(integral);
1711
1712 if (fractional_int == scale)
1713 {
1714 fractional_int = 0;
1715 ++integral_int;
1716
1717 if (integral_int == 10)
1718 {
1719 integral_int = 1;
1720 ++exponent_int;
1721 }
1722 }
1723
1724 private_format::format_sign<OutputIt, int>(it, sign ? -1 : 0, spec);
1725 private_format::format_plain_num<OutputIt, unsigned long long int>(it, integral_int, spec);
1726 private_format::format_sequence<OutputIt>(it, ".");
1727 private_format::format_plain_num<OutputIt, unsigned long long int>(it, fractional_int, spec, fractional_decimals);
1728 char letter = 'e';
1729 if (is_uppercase(spec.type.value()))
1730 {
1731 letter = 'E';
1732 }
1733 private_format::format_plain_char<OutputIt>(it, letter);
1734 private_format::format_plain_char<OutputIt>(it, (exponent_int < 0) ? '-' : '+');
1735 private_format::format_plain_num<OutputIt, long long int>(it, exponent_int, spec, exponent_decimals);
1736 }
1737
1738 template <typename OutputIt, typename T>
1739 void format_floating_f(OutputIt& it, T value, const format_spec_t& spec)
1740 {
1741 const size_t fractional_decimals = 6; // default
1742
1743 // Detect sign using signbit to correctly handle -0.0
1744 bool sign = signbit(value);
1745
1746 T integral;
1747 T fractional = format_modf(value, &integral);
1748
1749 // Take absolute values to avoid casting negative values to unsigned
1750 if (sign)
1751 {
1752 fractional = -fractional;
1753 integral = -integral;
1754 }
1755
1756 unsigned long long int scale = int_pow<unsigned long long int>(10, fractional_decimals);
1757 unsigned long long int fractional_int = static_cast<unsigned long long int>(format_round(fractional * scale));
1758 unsigned long long int integral_int = static_cast<unsigned long long int>(integral);
1759
1760 if (fractional_int == scale)
1761 {
1762 fractional_int = 0;
1763 ++integral_int;
1764 }
1765
1766 private_format::format_sign<OutputIt, int>(it, sign ? -1 : 0, spec);
1767 private_format::format_plain_num<OutputIt, unsigned long long int>(it, integral_int, spec);
1768 private_format::format_sequence<OutputIt>(it, ".");
1769 private_format::format_plain_num<OutputIt, unsigned long long int>(it, fractional_int, spec, fractional_decimals);
1770 }
1771 #endif
1772
1773 class dummy_assign_to
1774 {
1775 public:
1776
1777 dummy_assign_to& operator=(char_type)
1778 {
1779 return *this;
1780 }
1781 };
1782
1783 template <class OutputIt>
1784 class limit_assign_to
1785 {
1786 public:
1787
1788 limit_assign_to(OutputIt o, bool is_active)
1789 : out(o)
1790 , active(is_active)
1791 {
1792 }
1793
1794 limit_assign_to& operator=(char_type c)
1795 {
1796 if (active)
1797 {
1798 *out = c;
1799 }
1800 return *this;
1801 }
1802
1803 private:
1804
1805 OutputIt out;
1806 bool active;
1807 };
1808
1809 template <class OutputIt>
1810 class limit_iterator
1811 {
1812 public:
1813
1814 limit_iterator(OutputIt& it, size_t n)
1815 : out(it)
1816 , limit(n)
1817 {
1818 }
1819
1820 limit_iterator(const limit_iterator& other) = default;
1821 limit_iterator(limit_iterator&& other) = default;
1822 limit_iterator& operator=(const limit_iterator& other) = default;
1823 limit_iterator& operator=(limit_iterator&& other) = default;
1824
1825 limit_assign_to<OutputIt> operator*()
1826 {
1827 return limit_assign_to<OutputIt>(out, (limit > 0));
1828 }
1829
1830 limit_iterator& operator++()
1831 {
1832 if (limit > 0)
1833 {
1834 --limit;
1835 ++out;
1836 }
1837 return *this;
1838 }
1839
1840 limit_iterator operator++(int)
1841 {
1842 limit_iterator temp = *this;
1843 if (limit > 0)
1844 {
1845 --limit;
1846 out++;
1847 }
1848 return temp;
1849 }
1850
1851 OutputIt get()
1852 {
1853 return out;
1854 }
1855
1856 private:
1857
1858 OutputIt out;
1859 size_t limit;
1860 };
1861
1862 class counter_iterator
1863 {
1864 public:
1865
1866 counter_iterator()
1867 : count(0)
1868 {
1869 }
1870
1871 counter_iterator(const counter_iterator& other) = default;
1872 counter_iterator& operator=(const counter_iterator& other) = default;
1873
1874 dummy_assign_to operator*()
1875 {
1876 return dummy_assign_to();
1877 }
1878
1879 counter_iterator& operator++()
1880 {
1881 ++count;
1882 return *this;
1883 }
1884
1885 counter_iterator operator++(int)
1886 {
1887 counter_iterator temp = *this;
1888 count++;
1889 return temp;
1890 }
1891
1892 size_t value()
1893 {
1894 return count;
1895 }
1896
1897 private:
1898
1899 size_t count;
1900 };
1901
1902 #if ETL_USING_FORMAT_FLOATING_POINT
1903 template <typename OutputIt, typename T>
1904 void format_floating_g(OutputIt& it, T value, const format_spec_t& spec)
1905 {
1906 private_format::counter_iterator counter_e, counter_f;
1907
1908 format_floating_e(counter_e, value, spec);
1909 format_floating_f(counter_f, value, spec);
1910
1911 if (counter_e.value() < counter_f.value())
1912 {
1913 format_floating_e(it, value, spec);
1914 }
1915 else
1916 {
1917 format_floating_f(it, value, spec);
1918 }
1919 }
1920
1921 template <typename OutputIt, typename T>
1922 void format_floating(OutputIt& it, T value, const format_spec_t& spec)
1923 {
1924 if (isnan(value))
1925 {
1926 if (spec.type.has_value() && (is_uppercase(spec.type.value())))
1927 {
1928 format_sequence(it, "NAN");
1929 }
1930 else
1931 {
1932 format_sequence(it, "nan");
1933 }
1934 }
1935 else if (isinf(value))
1936 {
1937 if (spec.type.has_value() && (is_uppercase(spec.type.value())))
1938 {
1939 format_sequence(it, "INF");
1940 }
1941 else
1942 {
1943 format_sequence(it, "inf");
1944 }
1945 }
1946 else if (!spec.type.has_value())
1947 {
1948 format_floating_default(it, value, spec);
1949 }
1950 else
1951 {
1952 switch (spec.type.value())
1953 {
1954 case 'a':
1955 case 'A': format_floating_a(it, value, spec); break;
1956 case 'e':
1957 case 'E': format_floating_e(it, value, spec); break;
1958 case 'f':
1959 case 'F': format_floating_f(it, value, spec); break;
1960 case 'g':
1961 case 'G': format_floating_g(it, value, spec); break;
1962 default:
1963 // unknown presentation type
1964 ETL_ASSERT_FAIL(ETL_ERROR(bad_format_string_exception));
1965 }
1966 }
1967 }
1968 #endif
1969
1970 template <class OutputIt>
1971 struct format_visitor
1972 {
1973 using output_iterator = OutputIt;
1974
1975 format_visitor(format_parse_context& parse_context, format_context<OutputIt>& f_ctx)
1976 : parse_ctx(parse_context)
1977 , fmt_ctx(f_ctx)
1978 {
1979 }
1980
1981 // for all the built-in alternatives stored in basic_format_arg
1982 template <typename T>
1983 void operator()(T value)
1984 {
1985 formatter<T> f;
1986 format_parse_context::iterator it = f.parse(parse_ctx);
1987 parse_ctx.advance_to(it);
1988 OutputIt fit = f.format(value, fmt_ctx);
1989 fmt_ctx.advance_to(fit);
1990 }
1991
1992 // for user-defined types routed through basic_format_arg::handle
1993 void operator()(typename basic_format_arg<format_context<OutputIt> >::handle h)
1994 {
1995 h.format(parse_ctx, fmt_ctx);
1996 }
1997
1998 format_parse_context& parse_ctx;
1999 format_context<OutputIt>& fmt_ctx;
2000 };
2001
2002 template <class OutputIt>
2003 void output(format_context<OutputIt>& fmt_context, char c)
2004 {
2005 *fmt_context.out() = c;
2006 OutputIt tmp = fmt_context.out();
2007 tmp++;
2008 fmt_context.advance_to(tmp);
2009 }
2010
2011 // Visitor to extract an integer value as size_t from a format arg (for nested replacement fields)
2012 struct size_t_extractor
2013 {
2014 size_t value;
2015
2016 size_t_extractor()
2017 : value(0)
2018 {
2019 }
2020
2021 void operator()(int v)
2022 {
2023 value = static_cast<size_t>(v);
2024 }
2025 void operator()(unsigned int v)
2026 {
2027 value = static_cast<size_t>(v);
2028 }
2029 void operator()(long long int v)
2030 {
2031 value = static_cast<size_t>(v);
2032 }
2033 void operator()(unsigned long long int v)
2034 {
2035 value = static_cast<size_t>(v);
2036 }
2037
2038 // All other types are invalid for width/precision - ignore
2039 template <typename T>
2040 void operator()(T)
2041 {
2042 }
2043 };
2044
2045 // Resolve nested replacement fields for width and precision in the format spec.
2046 // When width_nested_replacement or precision_nested_replacement is true, the
2047 // width/precision value holds the arg index, which must be resolved to the actual value.
2048 template <class OutputIt>
2049 void resolve_nested_replacements(format_spec_t& spec, format_args<OutputIt>& args)
2050 {
2051 if (spec.width_nested_replacement && spec.width.has_value())
2052 {
2053 format_arg<OutputIt> width_arg = args.get(spec.width.value());
2054 size_t_extractor ext;
2055 width_arg.template visit<void>(ext);
2056 spec.width = ext.value;
2057 spec.width_nested_replacement = false;
2058 }
2059 if (spec.precision_nested_replacement && spec.precision.has_value())
2060 {
2061 format_arg<OutputIt> prec_arg = args.get(spec.precision.value());
2062 size_t_extractor ext;
2063 prec_arg.template visit<void>(ext);
2064 spec.precision = ext.value;
2065 spec.precision_nested_replacement = false;
2066 }
2067 }
2068
2069 // Compute prefix/suffix padding sizes for alignment.
2070 // default_align_start: if true, NONE defaults to left-align (START); otherwise right-align (END).
2071 inline void compute_padding(size_t pad, spec_align_t align, bool default_align_start, size_t& prefix_size, size_t& suffix_size)
2072 {
2073 switch (align)
2074 {
2075 case spec_align_t::START:
2076 prefix_size = 0;
2077 suffix_size = pad;
2078 break;
2079 case spec_align_t::CENTER:
2080 prefix_size = pad / 2;
2081 suffix_size = pad - prefix_size;
2082 break;
2083 case spec_align_t::END:
2084 prefix_size = pad;
2085 suffix_size = 0;
2086 break;
2087 case spec_align_t::NONE:
2088 default:
2089 if (default_align_start)
2090 {
2091 prefix_size = 0;
2092 suffix_size = pad;
2093 }
2094 else
2095 {
2096 prefix_size = pad;
2097 suffix_size = 0;
2098 }
2099 break;
2100 }
2101 }
2102
2103 template <typename OutputIt, typename Int>
2104 typename format_context<OutputIt>::iterator format_aligned_int(Int arg, format_context<OutputIt>& fmt_ctx)
2105 {
2106 size_t prefix_size = 0;
2107 size_t suffix_size = 0;
2108
2109 if (fmt_ctx.format_spec.width)
2110 {
2111 private_format::counter_iterator counter;
2112 private_format::format_num<private_format::counter_iterator, Int>(counter, arg, fmt_ctx.format_spec);
2113
2114 if (counter.value() < fmt_ctx.format_spec.width.value())
2115 {
2116 size_t pad = fmt_ctx.format_spec.width.value() - counter.value();
2117 compute_padding(pad, fmt_ctx.format_spec.align, false, prefix_size, suffix_size);
2118 }
2119 }
2120
2121 // actual output
2122 OutputIt it = fmt_ctx.out();
2123 private_format::fill<OutputIt>(it, prefix_size, fmt_ctx.format_spec.fill);
2124 private_format::format_num<OutputIt, Int>(it, arg, fmt_ctx.format_spec);
2125 private_format::fill<OutputIt>(it, suffix_size, fmt_ctx.format_spec.fill);
2126 return it;
2127 }
2128
2129 #if ETL_USING_FORMAT_FLOATING_POINT
2130 template <typename OutputIt, typename Float>
2131 typename format_context<OutputIt>::iterator format_aligned_floating(Float arg, format_context<OutputIt>& fmt_ctx)
2132 {
2133 size_t prefix_size = 0;
2134 size_t suffix_size = 0;
2135
2136 // For zero-padding ({:0Nf}), use '0' as fill and right-align (padding after sign)
2137 char_type fill_char = fmt_ctx.format_spec.fill;
2138 if (fmt_ctx.format_spec.zero && fmt_ctx.format_spec.align == spec_align_t::NONE)
2139 {
2140 fill_char = '0';
2141 }
2142
2143 if (fmt_ctx.format_spec.width)
2144 {
2145 private_format::counter_iterator counter;
2146 private_format::format_floating<private_format::counter_iterator, Float>(counter, arg, fmt_ctx.format_spec);
2147
2148 if (counter.value() < fmt_ctx.format_spec.width.value())
2149 {
2150 size_t pad = fmt_ctx.format_spec.width.value() - counter.value();
2151 if (fmt_ctx.format_spec.zero && fmt_ctx.format_spec.align == spec_align_t::NONE)
2152 {
2153 // Zero-padding: all padding goes between sign and digits
2154 prefix_size = pad;
2155 }
2156 else
2157 {
2158 compute_padding(pad, fmt_ctx.format_spec.align, false, prefix_size, suffix_size);
2159 }
2160 }
2161 }
2162
2163 // actual output
2164 OutputIt it = fmt_ctx.out();
2165
2166 if (fmt_ctx.format_spec.zero && fmt_ctx.format_spec.align == spec_align_t::NONE)
2167 {
2168 // Output sign first, then zero-fill, then the unsigned part
2169 bool sign = signbit(arg);
2170 if (sign || fmt_ctx.format_spec.sign != spec_sign_t::MINUS)
2171 {
2172 // Output the sign character
2173 char_type sc = '\0';
2174 if (sign)
2175 {
2176 sc = '-';
2177 }
2178 else
2179 {
2180 switch (fmt_ctx.format_spec.sign)
2181 {
2182 case spec_sign_t::PLUS: sc = '+'; break;
2183 case spec_sign_t::SPACE: sc = ' '; break;
2184 default: break;
2185 }
2186 }
2187 if (sc != '\0')
2188 {
2189 *it = sc;
2190 ++it;
2191 }
2192 }
2193 private_format::fill<OutputIt>(it, prefix_size, '0');
2194 // Format without sign (sign already emitted)
2195 format_spec_t no_sign_spec = fmt_ctx.format_spec;
2196 no_sign_spec.sign = spec_sign_t::MINUS;
2197 Float abs_arg = sign ? -arg : arg;
2198 private_format::format_floating<OutputIt, Float>(it, abs_arg, no_sign_spec);
2199 }
2200 else
2201 {
2202 private_format::fill<OutputIt>(it, prefix_size, fill_char);
2203 private_format::format_floating<OutputIt, Float>(it, arg, fmt_ctx.format_spec);
2204 private_format::fill<OutputIt>(it, suffix_size, fill_char);
2205 }
2206 return it;
2207 }
2208 #endif
2209
2210 template <typename OutputIt>
2211 void format_string_view(OutputIt& it, etl::string_view arg, const format_spec_t& spec)
2212 {
2213 bool escaped = false;
2214 if (spec.type.has_value())
2215 {
2216 switch (spec.type.value())
2217 {
2218 case 's':
2219 // default output
2220 break;
2221 case '?':
2222 // escaped string
2223 escaped = true;
2224 break;
2225 default:
2226 // invalid type for string
2227 ETL_ASSERT_FAIL(ETL_ERROR(bad_format_string_exception));
2228 }
2229 }
2230 size_t limit = etl::numeric_limits<size_t>::max();
2231 if (spec.precision.has_value())
2232 {
2233 limit = spec.precision.value();
2234 }
2235
2236 if (escaped)
2237 {
2238 format_plain_char(it, '"');
2239 }
2240 etl::string_view::const_iterator arg_it = arg.begin();
2241 while (arg_it != arg.cend() && limit > 0)
2242 {
2243 if (escaped)
2244 {
2245 format_escaped_char(it, *arg_it);
2246 }
2247 else
2248 {
2249 format_plain_char(it, *arg_it);
2250 }
2251 ++arg_it;
2252 --limit;
2253 }
2254 if (escaped)
2255 {
2256 format_plain_char(it, '"');
2257 }
2258 }
2259
2260 template <typename OutputIt>
2261 typename format_context<OutputIt>::iterator format_aligned_string_view(etl::string_view arg, format_context<OutputIt>& fmt_ctx)
2262 {
2263 size_t prefix_size = 0;
2264 size_t suffix_size = 0;
2265
2266 if (fmt_ctx.format_spec.width)
2267 {
2268 private_format::counter_iterator counter;
2269 private_format::format_string_view<private_format::counter_iterator>(counter, arg, fmt_ctx.format_spec);
2270
2271 if (counter.value() < fmt_ctx.format_spec.width.value())
2272 {
2273 size_t pad = fmt_ctx.format_spec.width.value() - counter.value();
2274 compute_padding(pad, fmt_ctx.format_spec.align, true, prefix_size, suffix_size);
2275 }
2276 }
2277
2278 // actual output
2279 OutputIt it = fmt_ctx.out();
2280 private_format::fill<OutputIt>(it, prefix_size, fmt_ctx.format_spec.fill);
2281 private_format::format_string_view<OutputIt>(it, arg, fmt_ctx.format_spec);
2282 private_format::fill<OutputIt>(it, suffix_size, fmt_ctx.format_spec.fill);
2283 return it;
2284 }
2285
2286 template <typename OutputIt>
2287 typename format_context<OutputIt>::iterator format_aligned_chars(const char* arg, format_context<OutputIt>& fmt_ctx)
2288 {
2289 return format_aligned_string_view<OutputIt>(etl::string_view(arg), fmt_ctx);
2290 }
2291
2292 inline void check_char_spec(const format_spec_t& spec)
2293 {
2294 if ((!spec.type.has_value() || spec.type.value() == 'c' || spec.type.value() == '?')
2295 && (spec.sign != spec_sign_t::MINUS || spec.zero || spec.hash || spec.precision))
2296 {
2297 ETL_ASSERT_FAIL(ETL_ERROR(bad_format_string_exception));
2298 }
2299 }
2300
2301 template <typename OutputIt>
2302 void format_char(OutputIt& it, char_type c, const format_spec_t& spec)
2303 {
2304 check_char_spec(spec);
2305 if (spec.type.has_value())
2306 {
2307 switch (spec.type.value())
2308 {
2309 case 'c':
2310 // default output
2311 format_plain_char(it, c);
2312 break;
2313 case '?':
2314 // escaped string
2315 format_plain_char(it, '\'');
2316 format_escaped_char(it, c);
2317 format_plain_char(it, '\'');
2318 break;
2319 case 'b':
2320 case 'B':
2321 case 'd':
2322 case 'o':
2323 case 'x':
2324 case 'X': private_format::format_num<OutputIt, unsigned int>(it, static_cast<unsigned int>(static_cast<unsigned char>(c)), spec); break;
2325 default:
2326 // invalid type for string
2327 ETL_ASSERT_FAIL(ETL_ERROR(bad_format_string_exception));
2328 }
2329 }
2330 else
2331 {
2332 format_plain_char(it, c);
2333 }
2334 }
2335
2336 template <typename OutputIt>
2337 typename format_context<OutputIt>::iterator format_aligned_char(char_type arg, format_context<OutputIt>& fmt_ctx)
2338 {
2339 size_t prefix_size = 0;
2340 size_t suffix_size = 0;
2341
2342 if (fmt_ctx.format_spec.width)
2343 {
2344 private_format::counter_iterator counter;
2345 private_format::format_char<private_format::counter_iterator>(counter, arg, fmt_ctx.format_spec);
2346
2347 if (counter.value() < fmt_ctx.format_spec.width.value())
2348 {
2349 size_t pad = fmt_ctx.format_spec.width.value() - counter.value();
2350 // char type defaults to left-align, integer presentation defaults to right-align
2351 bool default_start =
2352 !fmt_ctx.format_spec.type.has_value() || fmt_ctx.format_spec.type.value() == 'c' || fmt_ctx.format_spec.type.value() == '?';
2353 compute_padding(pad, fmt_ctx.format_spec.align, default_start, prefix_size, suffix_size);
2354 }
2355 }
2356
2357 // actual output
2358 OutputIt it = fmt_ctx.out();
2359 private_format::fill<OutputIt>(it, prefix_size, fmt_ctx.format_spec.fill);
2360 private_format::format_char<OutputIt>(it, arg, fmt_ctx.format_spec);
2361 private_format::fill<OutputIt>(it, suffix_size, fmt_ctx.format_spec.fill);
2362 return it;
2363 }
2364
2365 template <typename OutputIt>
2366 void format_bool(OutputIt& it, bool value, const format_spec_t& spec)
2367 {
2368 if (spec.type.has_value())
2369 {
2370 switch (spec.type.value())
2371 {
2372 case 's':
2373 // default output
2374 format_sequence(it, value ? "true" : "false");
2375 break;
2376 case 'b':
2377 case 'B':
2378 case 'd':
2379 case 'o':
2380 case 'x':
2381 case 'X': private_format::format_num<OutputIt, unsigned int>(it, static_cast<unsigned int>(static_cast<unsigned char>(value)), spec); break;
2382 default:
2383 // invalid type for string
2384 ETL_ASSERT_FAIL(ETL_ERROR(bad_format_string_exception));
2385 }
2386 }
2387 else
2388 {
2389 format_sequence(it, value ? "true" : "false");
2390 }
2391 }
2392
2393 template <typename OutputIt>
2394 typename format_context<OutputIt>::iterator format_aligned_bool(bool arg, format_context<OutputIt>& fmt_ctx)
2395 {
2396 size_t prefix_size = 0;
2397 size_t suffix_size = 0;
2398
2399 if (fmt_ctx.format_spec.width)
2400 {
2401 private_format::counter_iterator counter;
2402 private_format::format_bool<private_format::counter_iterator>(counter, arg, fmt_ctx.format_spec);
2403
2404 if (counter.value() < fmt_ctx.format_spec.width.value())
2405 {
2406 size_t pad = fmt_ctx.format_spec.width.value() - counter.value();
2407 compute_padding(pad, fmt_ctx.format_spec.align, false, prefix_size, suffix_size);
2408 }
2409 }
2410
2411 // actual output
2412 OutputIt it = fmt_ctx.out();
2413 private_format::fill<OutputIt>(it, prefix_size, fmt_ctx.format_spec.fill);
2414 private_format::format_bool<OutputIt>(it, arg, fmt_ctx.format_spec);
2415 private_format::fill<OutputIt>(it, suffix_size, fmt_ctx.format_spec.fill);
2416 return it;
2417 }
2418
2419 template <typename OutputIt>
2420 void format_pointer(OutputIt& it, const void* value, const format_spec_t& spec)
2421 {
2422 if (spec.type.has_value())
2423 {
2424 switch (spec.type.value())
2425 {
2426 case 'p':
2427 case 'P':
2428 format_sequence(it, spec.type.value() == 'p' ? "0x" : "0X");
2429 format_plain_num<OutputIt, uintptr_t>(it, reinterpret_cast<uintptr_t>(value), spec);
2430 break;
2431 default:
2432 // invalid type for string
2433 ETL_ASSERT_FAIL(ETL_ERROR(bad_format_string_exception));
2434 }
2435 }
2436 else
2437 {
2438 format_sequence(it, "0x");
2439 format_plain_num<OutputIt, uintptr_t>(it, reinterpret_cast<uintptr_t>(value), spec);
2440 }
2441 }
2442
2443 template <typename OutputIt>
2444 typename format_context<OutputIt>::iterator format_aligned_pointer(const void* arg, format_context<OutputIt>& fmt_ctx)
2445 {
2446 size_t prefix_size = 0;
2447 size_t suffix_size = 0;
2448
2449 if (fmt_ctx.format_spec.width)
2450 {
2451 private_format::counter_iterator counter;
2452 private_format::format_pointer<private_format::counter_iterator>(counter, arg, fmt_ctx.format_spec);
2453
2454 if (counter.value() < fmt_ctx.format_spec.width.value())
2455 {
2456 size_t pad = fmt_ctx.format_spec.width.value() - counter.value();
2457 compute_padding(pad, fmt_ctx.format_spec.align, false, prefix_size, suffix_size);
2458 }
2459 }
2460
2461 // actual output
2462 OutputIt it = fmt_ctx.out();
2463 private_format::fill<OutputIt>(it, prefix_size, fmt_ctx.format_spec.fill);
2464 private_format::format_pointer<OutputIt>(it, arg, fmt_ctx.format_spec);
2465 private_format::fill<OutputIt>(it, suffix_size, fmt_ctx.format_spec.fill);
2466 return it;
2467 }
2468 } // namespace private_format
2469
2470 template <>
2471 struct formatter<int>
2472 {
2473 format_parse_context::iterator parse(format_parse_context& parse_ctx)
2474 {
2475 // unified parsing is done already in vformat_to()
2476 return parse_ctx.begin();
2477 }
2478
2479 template <class OutputIt>
2480 typename format_context<OutputIt>::iterator format(int arg, format_context<OutputIt>& fmt_ctx)
2481 {
2482 if (fmt_ctx.format_spec.type.has_value() && fmt_ctx.format_spec.type.value() == 'c')
2483 {
2484 return private_format::format_aligned_char<OutputIt>(static_cast<private_format::char_type>(arg), fmt_ctx);
2485 }
2486 return private_format::format_aligned_int<OutputIt, int>(arg, fmt_ctx);
2487 }
2488 };
2489
2490 template <>
2491 struct formatter<unsigned int>
2492 {
2493 format_parse_context::iterator parse(format_parse_context& parse_ctx)
2494 {
2495 // unified parsing is done already in vformat_to()
2496 return parse_ctx.begin();
2497 }
2498
2499 template <class OutputIt>
2500 typename format_context<OutputIt>::iterator format(unsigned int arg, format_context<OutputIt>& fmt_ctx)
2501 {
2502 if (fmt_ctx.format_spec.type.has_value() && fmt_ctx.format_spec.type.value() == 'c')
2503 {
2504 return private_format::format_aligned_char<OutputIt>(static_cast<private_format::char_type>(arg), fmt_ctx);
2505 }
2506 return private_format::format_aligned_int<OutputIt, unsigned int>(arg, fmt_ctx);
2507 }
2508 };
2509
2510 template <>
2511 struct formatter<long long int>
2512 {
2513 format_parse_context::iterator parse(format_parse_context& parse_ctx)
2514 {
2515 // unified parsing is done already in vformat_to()
2516 return parse_ctx.begin();
2517 }
2518
2519 template <class OutputIt>
2520 typename format_context<OutputIt>::iterator format(long long int arg, format_context<OutputIt>& fmt_ctx)
2521 {
2522 if (fmt_ctx.format_spec.type.has_value() && fmt_ctx.format_spec.type.value() == 'c')
2523 {
2524 return private_format::format_aligned_char<OutputIt>(static_cast<private_format::char_type>(arg), fmt_ctx);
2525 }
2526 return private_format::format_aligned_int<OutputIt, long long int>(arg, fmt_ctx);
2527 }
2528 };
2529
2530 template <>
2531 struct formatter<unsigned long long int>
2532 {
2533 format_parse_context::iterator parse(format_parse_context& parse_ctx)
2534 {
2535 // unified parsing is done already in vformat_to()
2536 return parse_ctx.begin();
2537 }
2538
2539 template <class OutputIt>
2540 typename format_context<OutputIt>::iterator format(unsigned long long int arg, format_context<OutputIt>& fmt_ctx)
2541 {
2542 if (fmt_ctx.format_spec.type.has_value() && fmt_ctx.format_spec.type.value() == 'c')
2543 {
2544 return private_format::format_aligned_char<OutputIt>(static_cast<private_format::char_type>(arg), fmt_ctx);
2545 }
2546 return private_format::format_aligned_int<OutputIt, unsigned long long int>(arg, fmt_ctx);
2547 }
2548 };
2549
2550 template <>
2551 struct formatter<char>
2552 {
2553 format_parse_context::iterator parse(format_parse_context& parse_ctx)
2554 {
2555 // unified parsing is done already in vformat_to()
2556 return parse_ctx.begin();
2557 }
2558
2559 template <class OutputIt>
2560 typename format_context<OutputIt>::iterator format(private_format::char_type arg, format_context<OutputIt>& fmt_ctx)
2561 {
2562 return private_format::format_aligned_char<OutputIt>(arg, fmt_ctx);
2563 }
2564 };
2565
2566 #if ETL_USING_FORMAT_FLOATING_POINT
2567 template <>
2568 struct formatter<float>
2569 {
2570 format_parse_context::iterator parse(format_parse_context& parse_ctx)
2571 {
2572 // unified parsing is done already in vformat_to()
2573 return parse_ctx.begin();
2574 }
2575
2576 template <class OutputIt>
2577 typename format_context<OutputIt>::iterator format(float arg, format_context<OutputIt>& fmt_ctx)
2578 {
2579 return private_format::format_aligned_floating<OutputIt, float>(arg, fmt_ctx);
2580 }
2581 };
2582
2583 template <>
2584 struct formatter<double>
2585 {
2586 format_parse_context::iterator parse(format_parse_context& parse_ctx)
2587 {
2588 // unified parsing is done already in vformat_to()
2589 return parse_ctx.begin();
2590 }
2591
2592 template <class OutputIt>
2593 typename format_context<OutputIt>::iterator format(double arg, format_context<OutputIt>& fmt_ctx)
2594 {
2595 return private_format::format_aligned_floating<OutputIt, double>(arg, fmt_ctx);
2596 }
2597 };
2598
2599 template <>
2600 struct formatter<long double>
2601 {
2602 format_parse_context::iterator parse(format_parse_context& parse_ctx)
2603 {
2604 // unified parsing is done already in vformat_to()
2605 return parse_ctx.begin();
2606 }
2607
2608 template <class OutputIt>
2609 typename format_context<OutputIt>::iterator format(long double arg, format_context<OutputIt>& fmt_ctx)
2610 {
2611 return private_format::format_aligned_floating<OutputIt, long double>(arg, fmt_ctx);
2612 }
2613 };
2614 #endif
2615
2616 template <>
2617 struct formatter<etl::string_view>
2618 {
2619 format_parse_context::iterator parse(format_parse_context& parse_ctx)
2620 {
2621 // unified parsing is done already in vformat_to()
2622 return parse_ctx.begin();
2623 }
2624
2625 template <class OutputIt>
2626 typename format_context<OutputIt>::iterator format(etl::string_view arg, format_context<OutputIt>& fmt_ctx)
2627 {
2628 return private_format::format_aligned_string_view<OutputIt>(arg, fmt_ctx);
2629 }
2630 };
2631
2632 // string formatter
2633 template <>
2634 struct formatter<const char*>
2635 {
2636 format_parse_context::iterator parse(format_parse_context& parse_ctx)
2637 {
2638 // unified parsing is done already in vformat_to()
2639 return parse_ctx.begin();
2640 }
2641
2642 template <class OutputIt>
2643 typename format_context<OutputIt>::iterator format(const char* arg, format_context<OutputIt>& fmt_ctx)
2644 {
2645 return private_format::format_aligned_chars<OutputIt>(arg, fmt_ctx);
2646 }
2647 };
2648
2649 template <>
2650 struct formatter<bool>
2651 {
2652 format_parse_context::iterator parse(format_parse_context& parse_ctx)
2653 {
2654 // unified parsing is done already in vformat_to()
2655 return parse_ctx.begin();
2656 }
2657
2658 template <class OutputIt>
2659 typename format_context<OutputIt>::iterator format(bool arg, format_context<OutputIt>& fmt_ctx)
2660 {
2661 return private_format::format_aligned_bool<OutputIt>(arg, fmt_ctx);
2662 }
2663 };
2664
2665 template <>
2666 struct formatter<const void*>
2667 {
2668 format_parse_context::iterator parse(format_parse_context& parse_ctx)
2669 {
2670 // unified parsing is done already in vformat_to()
2671 return parse_ctx.begin();
2672 }
2673
2674 template <class OutputIt>
2675 typename format_context<OutputIt>::iterator format(const void* arg, format_context<OutputIt>& fmt_ctx)
2676 {
2677 return private_format::format_aligned_pointer<OutputIt>(arg, fmt_ctx);
2678 }
2679 };
2680
2681 template <class OutputIt>
2682 OutputIt vformat_to(OutputIt out, etl::string_view fmt, format_args<OutputIt> args)
2683 {
2684 format_parse_context parse_context(fmt, args.size());
2685 format_context<OutputIt> fmt_context(out, args);
2686 private_format::format_visitor<OutputIt> v(parse_context, fmt_context);
2687
2688 while (parse_context.begin() != parse_context.end())
2689 {
2690 const char c = *parse_context.begin();
2691 private_format::advance(parse_context);
2692 if (c == '{')
2693 {
2694 if (*parse_context.begin() == '{')
2695 {
2696 // escape sequence for literal '{'
2697 private_format::output<OutputIt>(fmt_context, c);
2698 private_format::advance(parse_context);
2699 }
2700 else
2701 {
2702 private_format::parse_format_spec<OutputIt>(parse_context, fmt_context);
2703
2704 // Resolve nested replacement fields for width/precision
2705 private_format::resolve_nested_replacements<OutputIt>(fmt_context.format_spec, args);
2706
2707 // Value index is always resolved in parse_format_spec
2708 size_t index = fmt_context.format_spec.index.value();
2709 format_arg<OutputIt> arg = args.get(index);
2710 arg.template visit<void>(v);
2711
2712 ETL_ASSERT(*parse_context.begin() == '}', ETL_ERROR(bad_format_string_exception) /*"Closing brace missing"*/);
2713 if (parse_context.begin() != parse_context.end())
2714 {
2715 private_format::advance(parse_context);
2716 }
2717 }
2718 }
2719 else if (c == '}') // only matches here if } without { is found
2720 {
2721 ETL_ASSERT(*parse_context.begin() == '}', ETL_ERROR(bad_format_string_exception) /*"2nd closing brace missing on escaped closing brace"*/);
2722 // escape sequence for literal '}'
2723 private_format::output<OutputIt>(fmt_context, c);
2724 private_format::advance(parse_context);
2725 }
2726 else
2727 {
2728 private_format::output<OutputIt>(fmt_context, c);
2729 }
2730 }
2731
2732 return fmt_context.out();
2733 }
2734
2735 template <typename OutputIt, typename = etl::enable_if_t< !etl::is_base_of< etl::remove_reference<etl::istring>::type, OutputIt>::value>,
2736 class... Args>
2737 OutputIt format_to(OutputIt out, format_string<Args...> fmt, Args&&... args)
2738 {
2739 auto the_args{make_format_args<OutputIt>(args...)};
2740 return vformat_to(etl::move(out), fmt.get(), format_args<OutputIt>(the_args));
2741 }
2742
2743 template <typename OutputIt, class WrapperIt = private_format::limit_iterator<OutputIt>, class... Args>
2744 OutputIt format_to_n(OutputIt out, size_t n, format_string<Args...> fmt, Args&&... args)
2745 {
2746 auto the_args{make_format_args<WrapperIt>(args...)};
2747 return vformat_to(WrapperIt(out, n), fmt.get(), format_args<WrapperIt>(the_args)).get();
2748 }
2749
2750 // non std in the following, specific to etl
2751 template <class... Args>
2752 etl::istring::iterator format_to(etl::istring& out, format_string<Args...> fmt, Args&&... args)
2753 {
2754 etl::istring::iterator result = format_to_n(out.begin(), out.max_size(), fmt, etl::forward<Args>(args)...);
2755 out.uninitialized_resize(static_cast<size_t>(result - out.begin()));
2756 return result;
2757 }
2758
2759 template <class... Args>
2760 size_t formatted_size(format_string<Args...> fmt, Args&&... args)
2761 {
2762 private_format::counter_iterator it;
2763 it = format_to(it, fmt, etl::forward<Args>(args)...);
2764 return it.value();
2765 }
2766} // namespace etl
2767
2768#endif
2769
2770#endif
ETL_CONSTEXPR14 basic_format_spec & fill(typename TString::value_type c) ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT
Definition basic_format_spec.h:462
ETL_CONSTEXPR14 basic_format_spec & width(uint32_t w) ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT
Definition basic_format_spec.h:381
ETL_CONSTEXPR14 basic_format_spec & precision(uint32_t p) ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT
Definition basic_format_spec.h:408
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 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:356
void uninitialized_resize(size_type new_size)
Definition basic_string.h:523
size_type max_size() const
Definition basic_string.h:223
#define ETL_ASSERT(b, e)
Definition error_handler.h:511
ETL_CONSTEXPR17 etl::enable_if<!etl::is_same< T, etl::nullptr_t >::value, T >::type * addressof(T &t)
Definition addressof.h:52
void * align(size_t alignment, size_t size, void *&ptr, size_t &space) ETL_NOEXCEPT
Definition memory.h:334
ETL_CONSTEXPR20_STL iterator begin() ETL_NOEXCEPT
Returns an iterator to the beginning of the optional.
Definition optional.h:1491
etl::monostate monostate
Definition variant_legacy.h:80
Definition absolute.h:40
ETL_CONSTEXPR TContainer::pointer data(TContainer &container)
Definition iterator.h:1470
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
integral_constant< bool, false > false_type
integral_constant specialisations
Definition type_traits.h:80
ETL_CONSTEXPR TContainer::size_type size(const TContainer &container)
Definition iterator.h:1434
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
TContainer::iterator end(TContainer &container)
Definition iterator.h:1166
T & get(array< T, Size > &a)
Definition array.h:1158
TContainer::iterator begin(TContainer &container)
Definition iterator.h:1136
A 'no-value' placeholder.
Definition monostate.h:42