forked from OctaForge/libostd
parse flags, width, precision and apply them in a few places
This commit is contained in:
parent
380990c129
commit
4bb689cdc2
257
octa/format.hh
257
octa/format.hh
|
@ -17,39 +17,81 @@
|
||||||
namespace octa {
|
namespace octa {
|
||||||
|
|
||||||
namespace detail {
|
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<typename R>
|
||||||
|
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 */
|
/* C string */
|
||||||
|
|
||||||
template<typename R>
|
template<typename R>
|
||||||
static inline octa::Size write_val(R &writer, const char *val) {
|
static inline octa::Size write_val(R &writer, const ArgParams &fl,
|
||||||
octa::Size n = 0;
|
const char *val) {
|
||||||
while (*val) {
|
octa::Size n = strlen(val);
|
||||||
writer.put(*val++);
|
octa::Size r = n;
|
||||||
++n;
|
r += fl.write_ws(writer, n, true);
|
||||||
}
|
while (*val) writer.put(*val++);
|
||||||
return n;
|
r += fl.write_ws(writer, n, false);
|
||||||
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* OctaSTD string */
|
/* OctaSTD string */
|
||||||
|
|
||||||
template<typename R, typename A>
|
template<typename R, typename A>
|
||||||
static inline octa::Size
|
static inline octa::Size
|
||||||
write_val(R &writer, const octa::AnyString<A> &val) {
|
write_val(R &writer, const ArgParams &fl, const octa::AnyString<A> &val) {
|
||||||
for (octa::Size i = 0; i < val.size(); ++i) writer.put(val[i]);
|
octa::Size n = val.size();
|
||||||
return 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 */
|
/* A character */
|
||||||
|
|
||||||
template<typename R>
|
template<typename R>
|
||||||
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);
|
writer.put(val);
|
||||||
|
r += fl.write_ws(writer, 1, false);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* boolean */
|
||||||
|
|
||||||
|
template<typename R>
|
||||||
|
static inline octa::Size write_val(R &writer, const ArgParams &fl,
|
||||||
|
bool val) {
|
||||||
|
write_val(writer, fl, val ? "true" : "false");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Integer helpers */
|
/* Integer helpers */
|
||||||
|
|
||||||
template<typename R>
|
template<typename R>
|
||||||
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];
|
char buf[20];
|
||||||
octa::Size n = 0;
|
octa::Size n = 0;
|
||||||
for (; val; val /= 10) buf[n++] = '0' + val % 10;
|
for (; val; val /= 10) buf[n++] = '0' + val % 10;
|
||||||
|
@ -60,7 +102,8 @@ namespace detail {
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename R>
|
template<typename R>
|
||||||
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;
|
octa::Uintmax uv;
|
||||||
int neg = 0;
|
int neg = 0;
|
||||||
if (val < 0) {
|
if (val < 0) {
|
||||||
|
@ -68,75 +111,209 @@ namespace detail {
|
||||||
writer.put('-');
|
writer.put('-');
|
||||||
neg = 1;
|
neg = 1;
|
||||||
} else uv = val;
|
} else uv = val;
|
||||||
return octa::detail::write_u(writer, uv) + neg;
|
return octa::detail::write_u(writer, fl, uv) + neg;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename R, typename T, bool = octa::IsIntegral<T>::value,
|
template<typename R, typename T, bool = octa::IsIntegral<T>::value,
|
||||||
bool = octa::IsSigned<T>::value>
|
bool = octa::IsSigned<T>::value>
|
||||||
struct WriteIntVal {
|
struct WriteIntVal {
|
||||||
static inline octa::Size write(R &writer, const T &val) {
|
static inline octa::Size write(R &writer, const ArgParams &fl,
|
||||||
return octa::detail::write_val(writer, octa::to_string(val));
|
const T &val) {
|
||||||
|
return octa::detail::write_val(writer, fl,
|
||||||
|
octa::to_string(val));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename R, typename T> struct WriteIntVal<R, T, true, true> {
|
template<typename R, typename T> struct WriteIntVal<R, T, true, true> {
|
||||||
static inline octa::Size write(R &writer, T val) {
|
static inline octa::Size write(R &writer, const ArgParams &fl, T val) {
|
||||||
return octa::detail::write_i(writer, val);
|
return octa::detail::write_i(writer, fl, val);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename R, typename T> struct WriteIntVal<R, T, true, false> {
|
template<typename R, typename T> struct WriteIntVal<R, T, true, false> {
|
||||||
static inline octa::Size write(R &writer, T val) {
|
static inline octa::Size write(R &writer, const ArgParams &fl, T val) {
|
||||||
return octa::detail::write_u(writer, val);
|
return octa::detail::write_u(writer, fl, val);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Integers or generic value */
|
/* Integers or generic value */
|
||||||
|
|
||||||
template<typename R, typename T>
|
template<typename R, typename T>
|
||||||
static inline octa::Size write_val(R &writer, const T &val) {
|
static inline octa::Size write_val(R &writer, const ArgParams &fl,
|
||||||
return octa::detail::WriteIntVal<R, T>::write(writer, val);
|
const T &val) {
|
||||||
|
return octa::detail::WriteIntVal<R, T>::write(writer, fl, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Writer entry point */
|
/* Writer entry point */
|
||||||
|
|
||||||
template<typename R, typename T, typename ...A>
|
template<typename R, typename T>
|
||||||
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) {
|
||||||
if (idx) assert(false && "not enough format args");
|
if (idx) assert(false && "not enough format args");
|
||||||
return octa::detail::write_val(writer, v);
|
return octa::detail::write_val(writer, fl, v);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename R, typename T, typename ...A>
|
template<typename R, typename T, typename ...A>
|
||||||
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) {
|
const A &...args) {
|
||||||
if (idx)
|
if (idx) return octa::detail::write_idx(writer, idx - 1, fl,
|
||||||
return octa::detail::write_idx(writer, idx - 1, args...);
|
args...);
|
||||||
return octa::detail::write_val(writer, v);
|
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<typename T>
|
||||||
|
static inline int conv_idx_num(const T &v, octa::EnableIf<
|
||||||
|
octa::IsIntegral<T>::value, bool
|
||||||
|
> = true) {
|
||||||
|
return int(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
static inline int conv_idx_num(const T &, octa::EnableIf<
|
||||||
|
!octa::IsIntegral<T>::value, bool
|
||||||
|
> = true) {
|
||||||
|
assert(false && "argument is not an integer");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
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<typename T, typename ...A>
|
||||||
|
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 */
|
} /* namespace detail */
|
||||||
|
|
||||||
template<typename R, typename ...A>
|
template<typename R, typename ...A>
|
||||||
static inline octa::Size formatted_write(R writer, octa::Size &fmtn,
|
static inline octa::Size formatted_write(R writer, octa::Size &fmtn,
|
||||||
const char *fmt, const A &...args) {
|
const char *fmt, const A &...args) {
|
||||||
|
char buf[32];
|
||||||
octa::Size argidx = 0, written = 0, retn = 0;
|
octa::Size argidx = 0, written = 0, retn = 0;
|
||||||
while (*fmt) {
|
while (*fmt) {
|
||||||
if (*fmt == '%') {
|
if (*fmt == '%') {
|
||||||
++fmt;
|
++fmt;
|
||||||
if (*fmt == '%') goto plain;
|
if (*fmt == '%') {
|
||||||
octa::Size needidx = argidx;
|
goto plain;
|
||||||
if (*fmt != 's') {
|
}
|
||||||
char idx[8] = { '\0' };
|
/* read digits: can be position, flags or width */
|
||||||
for (octa::Size i = 0; isdigit(*fmt); ++i)
|
octa::Size ndig = octa::detail::read_digits(fmt, buf);
|
||||||
idx[i] = *fmt++;
|
|
||||||
assert((*fmt == '$') && "malformed format string");
|
/* 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;
|
++fmt;
|
||||||
needidx = atoi(idx);
|
havepos = true;
|
||||||
} else ++argidx;
|
} 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");
|
assert((*fmt == 's') && "malformed format string");
|
||||||
++fmt;
|
++fmt;
|
||||||
retn = octa::max(needidx, retn);
|
retn = octa::max(argpos, retn);
|
||||||
written += octa::detail::write_idx(writer, needidx,
|
octa::detail::ArgParams afl = { flags, width, precision };
|
||||||
args...);
|
written += octa::detail::write_idx(writer, argpos, afl, args...);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
plain:
|
plain:
|
||||||
|
|
Loading…
Reference in a new issue