From 4bb689cdc226473d97bb3e42186524a8a76eafe0 Mon Sep 17 00:00:00 2001 From: q66 Date: Thu, 2 Jul 2015 02:00:05 +0100 Subject: [PATCH] parse flags, width, precision and apply them in a few places --- octa/format.hh | 257 +++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 217 insertions(+), 40 deletions(-) diff --git a/octa/format.hh b/octa/format.hh index b9306ca..733bf21 100644 --- a/octa/format.hh +++ b/octa/format.hh @@ -17,39 +17,81 @@ namespace octa { namespace detail { + enum FormatFlags { + FMT_LEFT_JUSTIFY = 1 << 0, + FMT_LEADING_SIGN = 1 << 1, + FMT_FORMAT_NUMBER = 1 << 2, + FMT_LEADING_ZEROS = 1 << 3, + FMT_LEADING_SPACE = 1 << 4 + }; + + struct ArgParams { + int flags; + int width; + int precision; + + template + octa::Size write_ws(R &writer, octa::Size n, bool left) const { + if (left == bool(flags & FMT_LEFT_JUSTIFY)) return 0; + int w = (int(n) > width) ? 0 : (width - int(n)); + octa::Size ret = w; + for (; w--; writer.put(' ')); + return ret; + } + }; + /* C string */ template - static inline octa::Size write_val(R &writer, const char *val) { - octa::Size n = 0; - while (*val) { - writer.put(*val++); - ++n; - } - return n; + static inline octa::Size write_val(R &writer, const ArgParams &fl, + const char *val) { + octa::Size n = strlen(val); + octa::Size r = n; + r += fl.write_ws(writer, n, true); + while (*val) writer.put(*val++); + r += fl.write_ws(writer, n, false); + return r; } /* OctaSTD string */ template static inline octa::Size - write_val(R &writer, const octa::AnyString &val) { - for (octa::Size i = 0; i < val.size(); ++i) writer.put(val[i]); - return val.size(); + write_val(R &writer, const ArgParams &fl, const octa::AnyString &val) { + octa::Size n = val.size(); + octa::Size r = n; + r += fl.write_ws(writer, n, true); + for (octa::Size i = 0; i < n; ++i) writer.put(val[i]); + r += fl.write_ws(writer, n, false); + return n; } /* A character */ template - static inline octa::Size write_val(R &writer, char val) { + static inline octa::Size write_val(R &writer, const ArgParams &fl, + char val) { + octa::Size r = 1; + r += fl.write_ws(writer, 1, true); writer.put(val); + r += fl.write_ws(writer, 1, false); + return r; + } + + /* boolean */ + + template + static inline octa::Size write_val(R &writer, const ArgParams &fl, + bool val) { + write_val(writer, fl, val ? "true" : "false"); return 1; } /* Integer helpers */ template - static inline octa::Size write_u(R &writer, octa::Uintmax val) { + static inline octa::Size write_u(R &writer, const ArgParams &, + octa::Uintmax val) { char buf[20]; octa::Size n = 0; for (; val; val /= 10) buf[n++] = '0' + val % 10; @@ -60,7 +102,8 @@ namespace detail { } template - static inline octa::Size write_i(R &writer, octa::Intmax val) { + static inline octa::Size write_i(R &writer, const ArgParams &fl, + octa::Intmax val) { octa::Uintmax uv; int neg = 0; if (val < 0) { @@ -68,75 +111,209 @@ namespace detail { writer.put('-'); neg = 1; } else uv = val; - return octa::detail::write_u(writer, uv) + neg; + return octa::detail::write_u(writer, fl, uv) + neg; } template::value, bool = octa::IsSigned::value> struct WriteIntVal { - static inline octa::Size write(R &writer, const T &val) { - return octa::detail::write_val(writer, octa::to_string(val)); + static inline octa::Size write(R &writer, const ArgParams &fl, + const T &val) { + return octa::detail::write_val(writer, fl, + octa::to_string(val)); } }; template struct WriteIntVal { - static inline octa::Size write(R &writer, T val) { - return octa::detail::write_i(writer, val); + static inline octa::Size write(R &writer, const ArgParams &fl, T val) { + return octa::detail::write_i(writer, fl, val); } }; template struct WriteIntVal { - static inline octa::Size write(R &writer, T val) { - return octa::detail::write_u(writer, val); + static inline octa::Size write(R &writer, const ArgParams &fl, T val) { + return octa::detail::write_u(writer, fl, val); } }; /* Integers or generic value */ template - static inline octa::Size write_val(R &writer, const T &val) { - return octa::detail::WriteIntVal::write(writer, val); + static inline octa::Size write_val(R &writer, const ArgParams &fl, + const T &val) { + return octa::detail::WriteIntVal::write(writer, fl, val); } /* Writer entry point */ - template - static inline octa::Size write_idx(R &writer, octa::Size idx, const T &v) { + template + static inline octa::Size write_idx(R &writer, octa::Size idx, + const ArgParams &fl, const T &v) { if (idx) assert(false && "not enough format args"); - return octa::detail::write_val(writer, v); + return octa::detail::write_val(writer, fl, v); } template - static inline octa::Size write_idx(R &writer, octa::Size idx, const T &v, + static inline octa::Size write_idx(R &writer, octa::Size idx, + const ArgParams &fl, const T &v, const A &...args) { - if (idx) - return octa::detail::write_idx(writer, idx - 1, args...); - return octa::detail::write_val(writer, v); + if (idx) return octa::detail::write_idx(writer, idx - 1, fl, + args...); + return octa::detail::write_val(writer, fl, v); } + + static inline int parse_fmt_flags(const char *&fmt, int ret) { + while (*fmt) { + switch (*fmt) { + case '-': ret |= FMT_LEFT_JUSTIFY; ++fmt; break; + case '+': ret |= FMT_LEADING_SIGN; ++fmt; break; + case '#': ret |= FMT_FORMAT_NUMBER; ++fmt; break; + case '0': ret |= FMT_LEADING_ZEROS; ++fmt; break; + case ' ': ret |= FMT_LEADING_SPACE; ++fmt; break; + default: goto retflags; + } + } + retflags: + return ret; + } + + static inline octa::Size read_digits(const char *&fmt, char *buf) { + octa::Size ret = 0; + for (; isdigit(*fmt); ++ret) + *buf++ = *fmt++; + *buf = '\0'; + return ret; + } + + template + static inline int conv_idx_num(const T &v, octa::EnableIf< + octa::IsIntegral::value, bool + > = true) { + return int(v); + } + + template + static inline int conv_idx_num(const T &, octa::EnableIf< + !octa::IsIntegral::value, bool + > = true) { + assert(false && "argument is not an integer"); + return 0; + } + + template + static inline int read_idx_num(octa::Size idx, const T &v) { + if (idx) assert(false && "not enough format args"); + return conv_idx_num(v); + } + + template + static inline int read_idx_num(octa::Size idx, const T &v, const A &...args) { + if (idx) return octa::detail::read_idx_num(idx - 1, args...); + return conv_idx_num(v); + } + } /* namespace detail */ template static inline octa::Size formatted_write(R writer, octa::Size &fmtn, const char *fmt, const A &...args) { + char buf[32]; octa::Size argidx = 0, written = 0, retn = 0; while (*fmt) { if (*fmt == '%') { ++fmt; - if (*fmt == '%') goto plain; - octa::Size needidx = argidx; - if (*fmt != 's') { - char idx[8] = { '\0' }; - for (octa::Size i = 0; isdigit(*fmt); ++i) - idx[i] = *fmt++; - assert((*fmt == '$') && "malformed format string"); + if (*fmt == '%') { + goto plain; + } + /* read digits: can be position, flags or width */ + octa::Size ndig = octa::detail::read_digits(fmt, buf); + + /* try if it's arg position */ + octa::Size argpos = argidx; + bool havepos = false; + if (*fmt == '$') { + assert((ndig > 0) && "no arg position given"); + if (ndig > 0) argpos = atoi(buf); ++fmt; - needidx = atoi(idx); + havepos = true; } else ++argidx; + + /* try and parse flags */ + int flags = 0; + octa::Size skipd = 0; + if (havepos || !ndig) { + flags = octa::detail::parse_fmt_flags(fmt, 0); + } else { + for (octa::Size i = 0; i < ndig; ++i) { + if (buf[i] != '0') break; + ++skipd; + } + if (skipd) flags = octa::detail::FMT_LEADING_ZEROS; + if (skipd == ndig) + flags = octa::detail::parse_fmt_flags(fmt, flags); + } + + /* try and parse width */ + int width = 0; + if (!havepos && ndig && (ndig - skipd)) + width = atoi(buf + skipd); + else if (octa::detail::read_digits(fmt, buf)) + width = atoi(buf); + else if (*fmt == '*') { + width = -1; + ++fmt; + } + + /* precision */ + int precision = 0; + if (*fmt != '.') goto fmtchar; + ++fmt; + if (octa::detail::read_digits(fmt, buf)) + precision = atoi(buf); + else if (*fmt == '*') { + precision = -1; + ++fmt; + } else assert(false && "no precision given"); + + /* expand precision and width */ + if (width < 0 || precision < 0) { + bool both = width < 0 && precision < 0; + if (havepos) { + if (argpos < (both ? 2 : 1)) { + if (width < 0) width = 0; + if (precision < 0) precision = 0; + assert(false && "invalid width argument"); + } else if (width < 0) { + if (precision < 0) { + width = octa::detail::read_idx_num( + argpos - 2, args...); + precision =octa::detail::read_idx_num( + argpos - 1, args...); + } else width = octa::detail::read_idx_num( + argpos - 1, args...); + } else precision = octa::detail::read_idx_num( + argpos - 1, args...); + } else { + if (width < 0) { + width = octa::detail::read_idx_num(argpos++, + args...); + ++argidx; + } + if (precision < 0) { + precision = octa::detail::read_idx_num(argpos++, + args...); + ++argidx; + } + } + } + + fmtchar: + /* parse needed position */ assert((*fmt == 's') && "malformed format string"); ++fmt; - retn = octa::max(needidx, retn); - written += octa::detail::write_idx(writer, needidx, - args...); + retn = octa::max(argpos, retn); + octa::detail::ArgParams afl = { flags, width, precision }; + written += octa::detail::write_idx(writer, argpos, afl, args...); continue; } plain: