Usermode/c++extras - Cleanup of cprintf code
[tpg/acess2.git] / Usermode / Libraries / libc++_extras.so_src / include_exp / cxxextras_printf
1 /*
2  */
3 #ifndef _LIBCXXEXTRAS_PRINTF_
4 #define _LIBCXXEXTRAS_PRINTF_
5
6 #include <cstddef>
7 #include <cstdlib>
8 #include <cstdio>
9 #include <functional>
10
11 namespace cxxextras {
12
13 class cprintf_toomanyargs:
14         public ::std::exception
15 {
16 };
17 class cprintf_toofewargs:
18         public ::std::exception
19 {
20 };
21 class cprintf_badformat:
22         public ::std::exception
23 {
24         const char *m_reason;
25 public:
26         cprintf_badformat(const char *reason):
27                 m_reason(reason)
28         {
29         }
30         const char* what() const noexcept override {
31                 return m_reason;
32         }
33 };
34
35 namespace _bits {
36
37 enum e_cprintf_type
38 {
39         TYPE_AUTO,
40         TYPE_BOOLEAN,
41         TYPE_BINARY,
42         TYPE_OCT,
43         TYPE_INT,
44         TYPE_INTU,
45         TYPE_INTS,
46         TYPE_HEXLC,
47         TYPE_HEXUC,
48         TYPE_STRING,
49 };
50
51 struct PrintfFlags
52 {
53         unsigned int width;
54         unsigned int precision;
55         unsigned int flags;
56         
57         struct Left {};
58         constexpr PrintfFlags(PrintfFlags x, Left  _): width(x.width), precision(x.precision), flags(x.flags | 1) {}
59         struct Sign {};
60         constexpr PrintfFlags(PrintfFlags x, Sign  _): width(x.width), precision(x.precision), flags(x.flags | 2) {}
61         struct Space {};
62         constexpr PrintfFlags(PrintfFlags x, Space _): width(x.width), precision(x.precision), flags(x.flags | 4) {}
63         struct Alt {};
64         constexpr PrintfFlags(PrintfFlags x, Alt   _): width(x.width), precision(x.precision), flags(x.flags | 8) {}
65         struct Zero {};
66         constexpr PrintfFlags(PrintfFlags x, Zero  _): width(x.width), precision(x.precision), flags(x.flags | 16) {}
67         
68         struct ArgWidth {};
69         constexpr PrintfFlags(PrintfFlags x, ArgWidth _, unsigned int v): width(v), precision(x.precision), flags(x.flags) {}
70         struct ArgPrec {};
71         constexpr PrintfFlags(PrintfFlags x, ArgPrec  _, unsigned int v): width(x.width), precision(v), flags(x.flags) {}
72         
73         struct FAuto {};
74         constexpr PrintfFlags(PrintfFlags x, FAuto   _): width(x.width), precision(x.precision), flags(x.flags | 0x000) {}
75         struct FString {};
76         constexpr PrintfFlags(PrintfFlags x, FString _): width(x.width), precision(x.precision), flags(x.flags | 0x100) {}
77         struct FBool {};
78         constexpr PrintfFlags(PrintfFlags x, FBool   _): width(x.width), precision(x.precision), flags(x.flags | 0x200) {}
79         struct FBinary {};
80         constexpr PrintfFlags(PrintfFlags x, FBinary _): width(x.width), precision(x.precision), flags(x.flags | 0x300) {}
81         struct FOct {};
82         constexpr PrintfFlags(PrintfFlags x, FOct   _): width(x.width), precision(x.precision), flags(x.flags | 0x400) {}
83         struct FUDec {};
84         constexpr PrintfFlags(PrintfFlags x, FUDec  _): width(x.width), precision(x.precision), flags(x.flags | 0x500) {}
85         struct FSDec {};
86         constexpr PrintfFlags(PrintfFlags x, FSDec  _): width(x.width), precision(x.precision), flags(x.flags | 0x600) {}
87         struct FHexL {};
88         constexpr PrintfFlags(PrintfFlags x, FHexL  _): width(x.width), precision(x.precision), flags(x.flags | 0x700) {}
89         struct FHexU {};
90         constexpr PrintfFlags(PrintfFlags x, FHexU  _): width(x.width), precision(x.precision), flags(x.flags | 0x800) {}
91         
92         PrintfFlags():
93                 width(0), precision(0), flags(0)
94         {
95         }
96 };
97 struct s_cprintf_fmt
98 {
99         bool isValid = false;
100         unsigned int precision = 0;
101         unsigned int minLength = 0;
102         bool    padLeft = false;
103         bool    showSign = false;
104         bool    showSpace = false;
105         bool    altFormat = false;
106         bool    padZero = false;
107         enum e_cprintf_type     type;
108 };
109 ;
110
111 constexpr bool isdigit_s(const char ch) {
112         return '0' <= ch && ch <= '9';
113 }
114 constexpr unsigned todigit(const char ch) {
115         return ch - '0';
116 }
117         
118
119 };      // namespace _bits
120
121 typedef ::std::function<void(const char*,size_t)>       cprintf_cb;
122 template <typename Arg> size_t cprintf_val(cprintf_cb puts, const _bits::PrintfFlags &fmt, Arg arg);
123
124 size_t cprintf_val(cprintf_cb puts, const _bits::PrintfFlags& fmt, const char* arg) {
125         unsigned int len;
126         for(len = 0; arg[len]; len ++)
127                 ;
128         puts(arg, len);
129         return len;
130 }
131 size_t cprintf_val(cprintf_cb puts, const _bits::PrintfFlags& fmt, int arg) {
132         size_t len = ::std::snprintf(nullptr, 0, "%i", arg);
133         char buf[len+1];
134         ::std::snprintf(buf, len+1, "%i", arg);
135         puts(buf, len);
136         return len;
137 }
138 size_t cprintf_val(cprintf_cb puts, const _bits::PrintfFlags& fmt, unsigned int arg) {
139         size_t len = ::std::snprintf(nullptr, 0, "%u", arg);
140         char buf[len+1];
141         ::std::snprintf(buf, len+1, "%u", arg);
142         puts(buf, len);
143         return len;
144 }
145
146 namespace _bits
147 {
148 namespace _printf
149 {
150         size_t run(cprintf_cb puts, const char *fmt);
151         template <typename... Args> size_t run(cprintf_cb puts, const char * fmt, Args... args);
152
153         // --- Print formatted value
154         template <typename Fmt>
155         constexpr size_t run_fmt_done(cprintf_cb puts, const char * fmt, PrintfFlags item, Fmt fmtcode)
156         {
157                 throw cprintf_toofewargs();
158         }
159         template <typename Fmt, typename Arg, typename... Args>
160         size_t run_fmt_done(cprintf_cb puts, const char * fmt, PrintfFlags item, Fmt fmtcode, Arg val,  Args... args)
161         {
162                 if( !puts )
163                         return run(puts, fmt+1, args...);
164                 else
165                         return ::cxxextras::cprintf_val(puts, PrintfFlags(item, fmtcode), val) + run(puts, fmt+1, args...);
166         }
167         // --- Format code
168         template <typename ...Args>
169         size_t run_fmt_fmt(cprintf_cb puts, const char * fmt, PrintfFlags item, Args... args)
170         {
171                 return *fmt == '\0' ? throw "ERROR: NUL byte in format specifier"
172                      : *fmt == '?' ? run_fmt_done(puts, fmt, item, PrintfFlags::FAuto(),  args...)
173                      : *fmt == 's' ? run_fmt_done(puts, fmt, item, PrintfFlags::FString(),args...)
174                      : *fmt == 'B' ? run_fmt_done(puts, fmt, item, PrintfFlags::FBool(),  args...)
175                      : *fmt == 'b' ? run_fmt_done(puts, fmt, item, PrintfFlags::FBinary(),args...)
176                      : *fmt == 'o' ? run_fmt_done(puts, fmt, item, PrintfFlags::FOct(),   args...)
177                      : *fmt == 'i' ? run_fmt_done(puts, fmt, item, PrintfFlags::FSDec(),  args...)
178                      : *fmt == 'u' ? run_fmt_done(puts, fmt, item, PrintfFlags::FUDec(),  args...)
179                      : *fmt == 'x' ? run_fmt_done(puts, fmt, item, PrintfFlags::FHexL(),  args...)
180                      : *fmt == 'X' ? run_fmt_done(puts, fmt, item, PrintfFlags::FHexU(),  args...)
181                      : throw cprintf_badformat("Unknown character in format string");
182         }
183         // --- Size modifier (not implemented, not needed?)
184         template <typename ...Args>
185         size_t run_fmt_size(cprintf_cb puts, const char * fmt, PrintfFlags item, Args... args)
186         {
187                 // TODO: Size characters?
188                 return run_fmt_fmt(puts, fmt, item, args...);
189         }
190         // --- Precision
191         template <typename ...Args>
192         size_t run_fmt_prec_val(cprintf_cb puts, const char * fmt, unsigned int val, PrintfFlags item, Args... args)
193         {
194                 return _bits::isdigit_s(*fmt) ? run_fmt_prec_val(puts, fmt+1, val*10+_bits::todigit(*fmt), item, args...)
195                      : run_fmt_size(puts, fmt, PrintfFlags(item, PrintfFlags::ArgPrec(), val), args...);
196         }
197         template <typename ...Args>
198         size_t run_fmt_prec_arg(cprintf_cb puts, const char *fmt, PrintfFlags item, unsigned int size, Args... args)
199         {
200                 return run_fmt_prec_opt(puts, fmt, PrintfFlags(item, PrintfFlags::ArgPrec(), size), args...);
201         }
202         template <typename Arg, typename ...Args>
203         size_t run_fmt_prec_arg(cprintf_cb puts, const char *fmt, PrintfFlags item, Arg size, Args... args)
204         {
205                 throw cprintf_badformat("Invalid type for printf precision modifier");
206         }
207         size_t run_fmt_prec_arg(cprintf_cb puts, const char *fmt, PrintfFlags item)
208         {
209                 throw cprintf_toofewargs();
210         }
211         template <typename ...Args>
212         size_t run_fmt_prec(cprintf_cb puts, const char * fmt, PrintfFlags item, Args... args)
213         {
214                 return _bits::isdigit_s(*fmt) ? run_fmt_prec_val(puts, fmt+1, _bits::todigit(*fmt), item, args...)
215                      : *fmt == '*' ? run_fmt_prec_arg(puts, fmt+1, item, args...)
216                      : run_fmt_size(puts, fmt, item, args...);
217         }
218         template <typename ...Args>
219         size_t run_fmt_prec_opt(cprintf_cb puts, const char * fmt, PrintfFlags item, Args... args)
220         {
221                 return *fmt == '.' ? run_fmt_prec(puts, fmt+1, item, args...)
222                      : run_fmt_size(puts, fmt, item, args...);
223         }
224         // --- Field Width ---  
225         template <typename ...Args>
226         size_t run_fmt_width_val(cprintf_cb puts, const char* fmt, unsigned int val, PrintfFlags item, Args... args)
227         {
228                 return  _bits::isdigit_s(*fmt) ? run_fmt_width_val(puts, fmt+1, val*10+_bits::todigit(*fmt), item, args...)
229                       : run_fmt_prec_opt(puts, fmt, PrintfFlags(item, PrintfFlags::ArgWidth(), val), args...);
230         }
231         template <typename ...Args>
232         size_t run_fmt_width_arg(cprintf_cb puts, const char *fmt, PrintfFlags item, unsigned int size, Args... args)
233         {
234                 return run_fmt_prec_opt(puts, fmt, PrintfFlags(item, PrintfFlags::ArgWidth(), size), args...);
235         }
236         template <typename Arg, typename ...Args>
237         size_t run_fmt_width_arg(cprintf_cb puts, const char *fmt, PrintfFlags item, Arg size, Args... args)
238         {
239                 throw cprintf_badformat("Invalid type for printf width modifier");
240         }
241         size_t run_fmt_width_arg(cprintf_cb puts, const char *fmt, PrintfFlags item)
242         {
243                 throw cprintf_toofewargs();
244         }
245         template <typename ...Args>
246         size_t run_fmt_width(cprintf_cb puts, const char * fmt, PrintfFlags item, Args... args)
247         {
248                 return _bits::isdigit_s(*fmt) ? run_fmt_width_val(puts, fmt+1, _bits::todigit(*fmt), item, args...)
249                      : *fmt == '*' ? run_fmt_width_arg(puts, fmt+1, item, args...)
250                      : run_fmt_prec_opt(puts, fmt, item, args...);
251         }
252         // --- Flags
253         template <typename ...Args>
254         size_t run_fmt_flags(cprintf_cb puts, const char * fmt, PrintfFlags item, Args... args)
255         {
256                 return
257                   *fmt == '-' ? run_fmt_flags(puts, fmt+1, PrintfFlags(item, PrintfFlags::Left()) , args...)
258                 : *fmt == '+' ? run_fmt_flags(puts, fmt+1, PrintfFlags(item, PrintfFlags::Sign()) , args...)
259                 : *fmt == ' ' ? run_fmt_flags(puts, fmt+1, PrintfFlags(item, PrintfFlags::Space()), args...)
260                 : *fmt == '#' ? run_fmt_flags(puts, fmt+1, PrintfFlags(item, PrintfFlags::Alt())  , args...)
261                 : *fmt == '0' ? run_fmt_flags(puts, fmt+1, PrintfFlags(item, PrintfFlags::Zero()) , args...)
262                 : run_fmt_width(puts, fmt, item, args...);
263         }
264         // --- Literal '%'
265         template <typename ...Args>
266         size_t run_fmt_start(cprintf_cb puts, const char * fmt, Args... args)
267         {
268                 return *fmt == '%'
269                         ? (puts("%", 1), 1 + run(puts, fmt+1, args...))
270                         : run_fmt_flags(puts, fmt, PrintfFlags(), args...);
271         }
272         // --- Root
273         template <typename... Args>
274         size_t run(cprintf_cb puts, const char * fmt, Args... args)
275         {
276                 int ofs;
277                 for( ofs = 0; fmt[ofs] != '\0' && fmt[ofs] != '%'; ofs ++ )
278                         ;
279                 if( fmt[ofs] != '%' )
280                         throw cprintf_toomanyargs();
281                 if(ofs > 0 && puts) puts(fmt, ofs);
282                 return ofs + run_fmt_start(puts, fmt+ofs+1, args...);
283         }
284         size_t run(cprintf_cb puts, const char *fmt)
285         {
286                 int ofs;
287                 for( ofs = 0; fmt[ofs] != '\0' && fmt[ofs] != '%'; ofs ++ )
288                         ;
289                 if(ofs > 0 && puts) puts(fmt, ofs);
290                 return ofs + (fmt[ofs] == '%' ? run_fmt_start(puts, fmt+ofs+1) : 0);
291         }
292
293 }
294
295 };      // namespace _bits
296
297 template <typename ... Args>
298 size_t cprintf(cprintf_cb puts, const char *fmt, Args... args)
299 {
300         _bits::_printf::run(nullptr, fmt, args...);
301         return _bits::_printf::run(puts, fmt, args...);
302 }
303
304 };
305
306 #endif
307
308 // vim: ft=cpp
309

UCC git Repository :: git.ucc.asn.au