diff options
Diffstat (limited to 'firmware/common/vuprintf.c')
-rw-r--r-- | firmware/common/vuprintf.c | 475 |
1 files changed, 465 insertions, 10 deletions
diff --git a/firmware/common/vuprintf.c b/firmware/common/vuprintf.c index 7dd9449e56..8bb9451662 100644 --- a/firmware/common/vuprintf.c +++ b/firmware/common/vuprintf.c | |||
@@ -23,8 +23,10 @@ | |||
23 | #include <limits.h> | 23 | #include <limits.h> |
24 | #include <string.h> | 24 | #include <string.h> |
25 | #include <stddef.h> | 25 | #include <stddef.h> |
26 | #include <stdlib.h> | ||
26 | #include "system.h" | 27 | #include "system.h" |
27 | #include "vuprintf.h" | 28 | #include "vuprintf.h" |
29 | #include "ap_int.h" | ||
28 | 30 | ||
29 | #ifndef BOOTLOADER | 31 | #ifndef BOOTLOADER |
30 | /* turn everything on */ | 32 | /* turn everything on */ |
@@ -58,7 +60,11 @@ | |||
58 | #define FMT_LENMOD_ll 0x010 /* signed/unsigned long long (%ll<radix>) */ | 60 | #define FMT_LENMOD_ll 0x010 /* signed/unsigned long long (%ll<radix>) */ |
59 | #define FMT_LENMOD_t 0x020 /* signed/unsigned ptrdiff_t (%t<radix>) */ | 61 | #define FMT_LENMOD_t 0x020 /* signed/unsigned ptrdiff_t (%t<radix>) */ |
60 | #define FMT_LENMOD_z 0x040 /* size_t/ssize_t (%z<radix>) */ | 62 | #define FMT_LENMOD_z 0x040 /* size_t/ssize_t (%z<radix>) */ |
63 | #if 0 | ||
61 | #define FMT_LENMOD_L 0x080 /* long double (instead of double) */ | 64 | #define FMT_LENMOD_L 0x080 /* long double (instead of double) */ |
65 | #else | ||
66 | #define FMT_LENMOD_L 0x000 | ||
67 | #endif | ||
62 | 68 | ||
63 | /* compulsory radixes: c, d, i, u, s */ | 69 | /* compulsory radixes: c, d, i, u, s */ |
64 | #define FMT_RADIX_c 0x001 /* single character (%c) */ | 70 | #define FMT_RADIX_c 0x001 /* single character (%c) */ |
@@ -75,6 +81,18 @@ | |||
75 | #define FMT_RADIX_g 0x800 /* floating point exponent or decimal depending | 81 | #define FMT_RADIX_g 0x800 /* floating point exponent or decimal depending |
76 | upon value and precision */ | 82 | upon value and precision */ |
77 | 83 | ||
84 | /* TODO: 'a' 'A' */ | ||
85 | #define FMT_RADIX_floats (FMT_RADIX_e|FMT_RADIX_f|FMT_RADIX_g) | ||
86 | |||
87 | #if (FMT_RADIX & FMT_RADIX_floats) | ||
88 | /* Assumes IEEE 754 double-precision, native-endian; replace to parse and init | ||
89 | for some other format */ | ||
90 | #define parse_double parse_ieee754_double | ||
91 | #define init_double_chunks init_ieee754_double_chunks | ||
92 | #define format_double_int10 format_ap_int10 | ||
93 | #define format_double_frac10 format_ap_frac10 | ||
94 | #endif | ||
95 | |||
78 | /* avoid defining redundant functions if two or more types can use the same | 96 | /* avoid defining redundant functions if two or more types can use the same |
79 | * something not getting a macro means it gets assigned its own value and | 97 | * something not getting a macro means it gets assigned its own value and |
80 | * formatter */ | 98 | * formatter */ |
@@ -374,8 +392,24 @@ struct fmt_buf { | |||
374 | or prefix (numeric) */ | 392 | or prefix (numeric) */ |
375 | char buf[24]; /* work buffer */ | 393 | char buf[24]; /* work buffer */ |
376 | char bufend[1]; /* buffer end marker and guard '0' */ | 394 | char bufend[1]; /* buffer end marker and guard '0' */ |
395 | #if (FMT_RADIX & FMT_RADIX_floats) | ||
396 | int lenmod; | ||
397 | int radixchar; | ||
398 | int signchar; | ||
399 | int alignchar; | ||
400 | int width; | ||
401 | int precision; | ||
402 | char *p; | ||
403 | #endif | ||
377 | }; | 404 | }; |
378 | 405 | ||
406 | #define PUSHCHAR(ch) \ | ||
407 | ({ int __rc = push(userp, (ch)); \ | ||
408 | count += __rc >= 0; \ | ||
409 | if (__rc <= 0) { \ | ||
410 | goto done; \ | ||
411 | } }) | ||
412 | |||
379 | /* %d %i */ | 413 | /* %d %i */ |
380 | static inline const char * format_d(int val, | 414 | static inline const char * format_d(int val, |
381 | struct fmt_buf *fmt_buf, | 415 | struct fmt_buf *fmt_buf, |
@@ -530,6 +564,400 @@ static inline const char * format_p(const void *p, | |||
530 | } | 564 | } |
531 | #endif /* FMT_RADIX_p */ | 565 | #endif /* FMT_RADIX_p */ |
532 | 566 | ||
567 | #if (FMT_RADIX & FMT_RADIX_floats) | ||
568 | /* find out how many uint32_t chunks need to be allocated, if any | ||
569 | * if none are needed, finish the init for the number here */ | ||
570 | static long parse_ieee754_double(double f, | ||
571 | struct ap_int *ia, | ||
572 | struct ap_int *fa, | ||
573 | struct fmt_buf *fmt_buf) | ||
574 | { | ||
575 | long rc = 0; | ||
576 | |||
577 | union { | ||
578 | double f; | ||
579 | uint64_t f64; | ||
580 | } u = { .f = f }; | ||
581 | |||
582 | int e = ((int)(u.f64 >> 52) & 0x7ff) - 1023; /* -1023..1024 */ | ||
583 | uint64_t mantissa = u.f64 & 0x000fffffffffffffull; | ||
584 | |||
585 | if (u.f64 >> 63) { | ||
586 | fmt_buf->signchar = '-'; | ||
587 | } | ||
588 | |||
589 | if (LIKELY(e >= -8 && e <= 63)) { /* -8 to +63 */ | ||
590 | /* integer, fraction and manipulations fit in uint64_t */ | ||
591 | mantissa |= 0x0010000000000000ull; | ||
592 | ia->numchunks = 0; | ||
593 | ia->shift = 0; | ||
594 | fa->numchunks = 0; | ||
595 | |||
596 | if (e < 0) { /* -8 to -1 - fraction */ | ||
597 | long fracbits = 52 - e; | ||
598 | /* int - none */ | ||
599 | ia->len = 0; | ||
600 | ia->val = 0; | ||
601 | /* frac */ | ||
602 | fa->len = fracbits - __builtin_ctzll(mantissa); | ||
603 | fa->shift = fracbits; | ||
604 | fa->val = mantissa; | ||
605 | } | ||
606 | else if (e <= 51) { /* 0 to +51 - integer|fraction */ | ||
607 | long fracbits = 52 - e; | ||
608 | /* int */ | ||
609 | ia->len = base10exp(e) + 2; /* go up + possibly 1 longer */ | ||
610 | ia->val = mantissa >> fracbits; | ||
611 | /* frac */ | ||
612 | fa->shift = fracbits; | ||
613 | fa->val = mantissa ^ (ia->val << fracbits); | ||
614 | fa->len = fa->val ? fracbits - __builtin_ctzll(mantissa) : 0; | ||
615 | } | ||
616 | else { /* +52 to +63 - integer */ | ||
617 | /* int */ | ||
618 | ia->len = base10exp(e) + 2; | ||
619 | ia->val = mantissa << (e - 52); | ||
620 | /* frac - none */ | ||
621 | fa->len = 0; | ||
622 | fa->shift = 0; | ||
623 | fa->val = 0; | ||
624 | } | ||
625 | } | ||
626 | else if (e < 0) { /* -1023 to -9 - fraction */ | ||
627 | /* int - none */ | ||
628 | ia->numchunks = 0; | ||
629 | ia->len = 0; | ||
630 | ia->shift = 0; | ||
631 | ia->val = 0; | ||
632 | /* frac - left-justify on bit 31 of the chunk of the MSb */ | ||
633 | if (e >= -1022) { /* normal */ | ||
634 | mantissa |= 0x0010000000000000ull; | ||
635 | } | ||
636 | else { /* subnormal (including zero) */ | ||
637 | e = -1022; | ||
638 | } | ||
639 | |||
640 | if (mantissa) { | ||
641 | long fracbits = 52 - e; | ||
642 | fa->len = fracbits - __builtin_ctzll(mantissa); | ||
643 | fa->shift = 31 - ((51 - e) % 32); | ||
644 | fa->val = mantissa; | ||
645 | fa->basechunk = (fa->shift + 52) / 32; | ||
646 | fa->numchunks = (51 - e + fa->shift) / 32 + 1; | ||
647 | rc = fa->numchunks; | ||
648 | } | ||
649 | else { /* zero */ | ||
650 | fa->numchunks = 0; | ||
651 | fa->len = 0; | ||
652 | fa->shift = 0; | ||
653 | fa->val = 0; | ||
654 | } | ||
655 | } | ||
656 | else if (e <= 1023) { /* +64 to +1023 - integer */ | ||
657 | /* int - right-justify on bit 0 of the first chunk */ | ||
658 | ia->val = mantissa | 0x0010000000000000ull; | ||
659 | ia->len = base10exp(e) + 2; | ||
660 | ia->shift = (e - 52) % 32; | ||
661 | ia->basechunk = e / 32; | ||
662 | ia->numchunks = ia->basechunk + 1; | ||
663 | rc = ia->numchunks; | ||
664 | /* frac - none */ | ||
665 | fa->numchunks = 0; | ||
666 | fa->len = 0; | ||
667 | fa->shift = 0; | ||
668 | fa->val = 0; | ||
669 | } | ||
670 | else { /* +1024: INF, NAN */ | ||
671 | rc = -1 - !!mantissa; | ||
672 | } | ||
673 | |||
674 | return rc; | ||
675 | } | ||
676 | |||
677 | /* construct the arbitrary-precision value in the provided allocation */ | ||
678 | static void init_ieee754_double_chunks(struct ap_int *a, | ||
679 | uint32_t *a_chunks) | ||
680 | { | ||
681 | long basechunk = a->basechunk; | ||
682 | long shift = a->shift; | ||
683 | uint64_t val = a->val; | ||
684 | |||
685 | a->chunks = a_chunks; | ||
686 | |||
687 | memset(a_chunks, 0, a->numchunks*sizeof (uint32_t)); | ||
688 | |||
689 | if (shift < 12) { | ||
690 | a_chunks[basechunk - 1] = val << shift; | ||
691 | a_chunks[basechunk - 0] = val >> (32 - shift); | ||
692 | } | ||
693 | else { | ||
694 | a_chunks[basechunk - 2] = val << shift; | ||
695 | a_chunks[basechunk - 1] = val >> (32 - shift); | ||
696 | a_chunks[basechunk - 0] = val >> (64 - shift); | ||
697 | } | ||
698 | } | ||
699 | |||
700 | /* format inf, nan strings */ | ||
701 | static void format_inf_nan(struct fmt_buf *fmt_buf, long type) | ||
702 | { | ||
703 | /* certain special values */ | ||
704 | static const char text[2][2][3] = | ||
705 | { | ||
706 | { { 'I', 'N', 'F' }, { 'i', 'n', 'f' } }, | ||
707 | { { 'N', 'A', 'N' }, { 'n', 'a', 'n' } }, | ||
708 | }; | ||
709 | |||
710 | char *p = fmt_buf->buf; | ||
711 | fmt_buf->p = p; | ||
712 | fmt_buf->length = 3; | ||
713 | |||
714 | /* they also have a sign */ | ||
715 | if (fmt_buf->signchar) { | ||
716 | *p++ = fmt_buf->signchar; | ||
717 | fmt_buf->length++; | ||
718 | } | ||
719 | |||
720 | memcpy(p, &text[type][(fmt_buf->radixchar >> 5) & 0x1], 3); | ||
721 | } | ||
722 | |||
723 | /* %e %E %f %F %g %G */ | ||
724 | static int format_double_radix(double f, | ||
725 | struct fmt_buf *fmt_buf, | ||
726 | vuprintf_push_cb push, | ||
727 | void *userp) | ||
728 | { | ||
729 | struct ap_int ia, fa; | ||
730 | long rc = parse_double(f, &ia, &fa, fmt_buf); | ||
731 | |||
732 | if (UNLIKELY(rc < 0)) { | ||
733 | format_inf_nan(fmt_buf, -rc - 1); | ||
734 | return 0; | ||
735 | } | ||
736 | |||
737 | int count = 0; | ||
738 | |||
739 | /* default precision is 6 for all formats */ | ||
740 | int prec_rem = fmt_buf->precision < 0 ? 6 : fmt_buf->precision; | ||
741 | |||
742 | int exp = exp; | ||
743 | int explen = 0; | ||
744 | |||
745 | switch (fmt_buf->radixchar & 0x3) | ||
746 | { | ||
747 | case 3: /* %g, %G */ | ||
748 | fmt_buf->precision = prec_rem; | ||
749 | if (prec_rem) { | ||
750 | prec_rem--; | ||
751 | } | ||
752 | case 1: /* %e, %E */ | ||
753 | explen = 2; | ||
754 | break; | ||
755 | default: | ||
756 | break; | ||
757 | } | ||
758 | |||
759 | if (rc > 0 && ia.numchunks > 0) { | ||
760 | /* large integer required */ | ||
761 | init_double_chunks(&ia, alloca(rc*sizeof(*ia.chunks))); | ||
762 | rc = 0; | ||
763 | } | ||
764 | |||
765 | const int bufoffs = 6; /* log rollover + round rollover + leading zeros (%g) */ | ||
766 | long f_prec = MIN(fa.len, prec_rem + 1); | ||
767 | char buf[bufoffs + ia.len + f_prec + 1]; | ||
768 | char *p_last = &buf[bufoffs + ia.len]; | ||
769 | char *p_dec = p_last; | ||
770 | char *p_first = format_double_int10(&ia, p_last); | ||
771 | |||
772 | if (explen) { | ||
773 | if (!ia.val && fa.len) { | ||
774 | p_first = p_last = &buf[bufoffs]; | ||
775 | f_prec = -f_prec - 1; /* no lead zeros */ | ||
776 | } | ||
777 | else { /* handles 0e+0 too */ | ||
778 | exp = ia.len - 1; | ||
779 | |||
780 | if (exp) { | ||
781 | prec_rem -= exp; | ||
782 | |||
783 | if (prec_rem < 0) { | ||
784 | p_last += prec_rem + 1; | ||
785 | f_prec = 0; | ||
786 | } | ||
787 | else { | ||
788 | f_prec = MIN(fa.len, prec_rem + 1); | ||
789 | } | ||
790 | } | ||
791 | } | ||
792 | |||
793 | p_dec = p_first + 1; | ||
794 | } | ||
795 | |||
796 | if (f_prec) { | ||
797 | if (rc > 0) { | ||
798 | /* large integer required */ | ||
799 | init_double_chunks(&fa, alloca(rc*sizeof(*fa.chunks))); | ||
800 | } | ||
801 | |||
802 | p_last = format_double_frac10(&fa, p_last, f_prec); | ||
803 | |||
804 | if (f_prec < 0) { | ||
805 | f_prec = -f_prec - 1; | ||
806 | exp = f_prec - fa.len; | ||
807 | } | ||
808 | |||
809 | prec_rem -= f_prec; | ||
810 | } | ||
811 | |||
812 | if (prec_rem < 0) { | ||
813 | prec_rem = 0; | ||
814 | p_last--; | ||
815 | |||
816 | if (round_number_string10(p_last, p_last - p_first)) { | ||
817 | /* carried left */ | ||
818 | p_first--; | ||
819 | |||
820 | if (explen) { | ||
821 | /* slide everything left by 1 */ | ||
822 | exp++; | ||
823 | p_dec--; | ||
824 | p_last--; | ||
825 | } | ||
826 | } | ||
827 | } | ||
828 | |||
829 | if (explen) { | ||
830 | if ((fmt_buf->radixchar & 0x3) == 0x3) { /* g, G */ | ||
831 | /* 'g' is some weird crap */ | ||
832 | /* now that the final exponent is known and everything rounded, | ||
833 | it is possible to decide whether to format similarly to | ||
834 | 'e' or 'f' */ | ||
835 | if (fmt_buf->precision > exp && exp >= -4) { /* P > X >= -4 */ | ||
836 | if (exp >= 0) { | ||
837 | /* integer digits will be in the buffer */ | ||
838 | p_dec = p_first + exp + 1; | ||
839 | } | ||
840 | else { | ||
841 | /* we didn't keep leading zeros and need to regenerate | ||
842 | them; space was reserved just in case */ | ||
843 | p_first = memset(p_dec + exp - 1, '0', -exp); | ||
844 | p_dec = p_first + 1; | ||
845 | } | ||
846 | |||
847 | /* suppress exponent */ | ||
848 | explen = 0; | ||
849 | } | ||
850 | |||
851 | if (!fmt_buf->length) { | ||
852 | /* strip any trailing zeros from the fraction */ | ||
853 | while (p_last > p_dec && p_last[-1] == '0') { | ||
854 | p_last--; | ||
855 | } | ||
856 | |||
857 | /* suppress trailing precision fill */ | ||
858 | prec_rem = 0; | ||
859 | } | ||
860 | } | ||
861 | |||
862 | if (explen) { | ||
863 | /* build exponent string: 'eħdd' */ | ||
864 | char *p = fmt_buf->bufend; | ||
865 | int signchar = '+'; | ||
866 | |||
867 | if (exp < 0) { | ||
868 | signchar = '-'; | ||
869 | exp = -exp; | ||
870 | } | ||
871 | |||
872 | while (exp || explen < 4) { | ||
873 | *--p = exp % 10 + '0'; | ||
874 | exp /= 10; | ||
875 | explen++; | ||
876 | } | ||
877 | |||
878 | *--p = signchar; | ||
879 | *--p = fmt_buf->radixchar & ~0x2; | ||
880 | } | ||
881 | } | ||
882 | |||
883 | int width = fmt_buf->width; | ||
884 | int point = p_last > p_dec || prec_rem || fmt_buf->length; | ||
885 | int length = p_last - p_first + !!fmt_buf->signchar + point + explen; | ||
886 | |||
887 | if (width) { | ||
888 | if (width - length <= prec_rem) { | ||
889 | width = 0; | ||
890 | } | ||
891 | else { | ||
892 | width -= length + prec_rem; | ||
893 | } | ||
894 | } | ||
895 | |||
896 | rc = -1; | ||
897 | |||
898 | /* left padding */ | ||
899 | if (fmt_buf->alignchar > '0') { | ||
900 | /* space-padded width -- before sign */ | ||
901 | while (width > 0) { | ||
902 | PUSHCHAR(' '); | ||
903 | width--; | ||
904 | } | ||
905 | } | ||
906 | |||
907 | if (fmt_buf->signchar) { | ||
908 | PUSHCHAR(fmt_buf->signchar); | ||
909 | } | ||
910 | |||
911 | if (fmt_buf->alignchar == '0') { | ||
912 | /* zero-padded width -- after sign */ | ||
913 | while (width > 0) { | ||
914 | PUSHCHAR('0'); | ||
915 | width--; | ||
916 | } | ||
917 | } | ||
918 | |||
919 | /* integer part */ | ||
920 | while (p_first < p_dec) { | ||
921 | PUSHCHAR(*p_first++); | ||
922 | } | ||
923 | |||
924 | /* decimal point */ | ||
925 | if (point) { | ||
926 | PUSHCHAR('.'); | ||
927 | } | ||
928 | |||
929 | /* fractional part */ | ||
930 | while (p_first < p_last) { | ||
931 | PUSHCHAR(*p_first++); | ||
932 | } | ||
933 | |||
934 | /* precision 0-padding */ | ||
935 | while (prec_rem > 0) { | ||
936 | PUSHCHAR('0'); | ||
937 | prec_rem--; | ||
938 | } | ||
939 | |||
940 | /* exponent */ | ||
941 | if (explen > 0) { | ||
942 | char *p = fmt_buf->bufend; | ||
943 | while (explen > 0) { | ||
944 | PUSHCHAR(p[-explen--]); | ||
945 | } | ||
946 | } | ||
947 | |||
948 | /* right padding */ | ||
949 | while (width > 0) { | ||
950 | PUSHCHAR(' '); | ||
951 | width--; | ||
952 | } | ||
953 | |||
954 | rc = 1; | ||
955 | done: | ||
956 | fmt_buf->length = count; | ||
957 | return rc; | ||
958 | } | ||
959 | #endif /* FMT_RADIX_floats */ | ||
960 | |||
533 | /* parse fixed width or precision field */ | 961 | /* parse fixed width or precision field */ |
534 | static const char * parse_number_spec(const char *fmt, | 962 | static const char * parse_number_spec(const char *fmt, |
535 | int ch, | 963 | int ch, |
@@ -558,16 +986,6 @@ int vuprintf(vuprintf_push_cb push, /* call 'push()' for each output letter */ | |||
558 | const char *fmt, | 986 | const char *fmt, |
559 | va_list ap) | 987 | va_list ap) |
560 | { | 988 | { |
561 | #define PUSHCHAR(ch) \ | ||
562 | do { \ | ||
563 | int __ch = (ch); \ | ||
564 | int __rc = push(userp, __ch); \ | ||
565 | count += __rc >= 0; \ | ||
566 | if (__rc <= 0) { \ | ||
567 | goto done; \ | ||
568 | } \ | ||
569 | } while (0) | ||
570 | |||
571 | int count = 0; | 989 | int count = 0; |
572 | int ch; | 990 | int ch; |
573 | 991 | ||
@@ -706,6 +1124,9 @@ int vuprintf(vuprintf_push_cb push, /* call 'push()' for each output letter */ | |||
706 | #if (FMT_LENMOD & FMT_LENMOD_z) | 1124 | #if (FMT_LENMOD & FMT_LENMOD_z) |
707 | case 'z': | 1125 | case 'z': |
708 | #endif | 1126 | #endif |
1127 | #if (FMT_LENMOD & FMT_LENMOD_L) | ||
1128 | case 'L': | ||
1129 | #endif | ||
709 | lenmod = ch; | 1130 | lenmod = ch; |
710 | ch = *fmt++; | 1131 | ch = *fmt++; |
711 | #if (FMT_LENMOD & (FMT_LENMOD_hh | FMT_LENMOD_ll)) | 1132 | #if (FMT_LENMOD & (FMT_LENMOD_hh | FMT_LENMOD_ll)) |
@@ -747,6 +1168,40 @@ int vuprintf(vuprintf_push_cb push, /* call 'push()' for each output letter */ | |||
747 | break; | 1168 | break; |
748 | #endif | 1169 | #endif |
749 | 1170 | ||
1171 | #if (FMT_RADIX & FMT_RADIX_floats) | ||
1172 | /* any floats gets all of them (except with 'L' and %a, %A for now) */ | ||
1173 | case 'e': | ||
1174 | case 'E': | ||
1175 | case 'f': | ||
1176 | case 'F': | ||
1177 | case 'g': | ||
1178 | case 'G': | ||
1179 | /* LENMOD_L isn't supported for now and will be rejected automatically */ | ||
1180 | |||
1181 | /* floating point has very different spec interpretations to other | ||
1182 | formats and requires special handling */ | ||
1183 | fmt_buf.length = pfxlen; | ||
1184 | fmt_buf.lenmod = lenmod; | ||
1185 | fmt_buf.radixchar = ch; | ||
1186 | fmt_buf.signchar = signchar; | ||
1187 | fmt_buf.alignchar = alignchar; | ||
1188 | fmt_buf.width = width; | ||
1189 | fmt_buf.precision = precision; | ||
1190 | |||
1191 | ch = format_double_radix(va_arg(ap, double), &fmt_buf, push, userp); | ||
1192 | if (ch) { | ||
1193 | count += fmt_buf.length; | ||
1194 | if (ch > 0) { | ||
1195 | continue; | ||
1196 | } | ||
1197 | |||
1198 | goto done; | ||
1199 | } | ||
1200 | |||
1201 | buf = fmt_buf.p; | ||
1202 | break; | ||
1203 | #endif | ||
1204 | |||
750 | /** signed integer **/ | 1205 | /** signed integer **/ |
751 | case 'd': | 1206 | case 'd': |
752 | case 'i': | 1207 | case 'i': |