2015-07-04 18:19:49 +00:00
|
|
|
/* Format strings for OctaSTD. Inspired by D's std.format module.
|
2015-07-01 01:22:42 +00:00
|
|
|
*
|
|
|
|
* This file is part of OctaSTD. See COPYING.md for futher information.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifndef OCTA_FORMAT_HH
|
|
|
|
#define OCTA_FORMAT_HH
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <ctype.h>
|
|
|
|
#include <assert.h>
|
|
|
|
|
|
|
|
#include "octa/algorithm.hh"
|
|
|
|
#include "octa/string.hh"
|
2015-07-04 13:52:02 +00:00
|
|
|
#include "octa/utility.hh"
|
2015-07-01 01:22:42 +00:00
|
|
|
|
|
|
|
namespace octa {
|
|
|
|
|
2015-07-02 18:02:59 +00:00
|
|
|
enum FormatFlags {
|
|
|
|
FMT_FLAG_DASH = 1 << 0,
|
|
|
|
FMT_FLAG_ZERO = 1 << 1,
|
|
|
|
FMT_FLAG_SPACE = 1 << 2,
|
|
|
|
FMT_FLAG_PLUS = 1 << 3,
|
|
|
|
FMT_FLAG_HASH = 1 << 4
|
|
|
|
};
|
|
|
|
|
2015-07-01 01:29:42 +00:00
|
|
|
namespace detail {
|
2015-07-11 15:07:52 +00:00
|
|
|
inline int parse_fmt_flags(const char *&fmt, int ret) {
|
2015-07-02 18:02:59 +00:00
|
|
|
while (*fmt) {
|
|
|
|
switch (*fmt) {
|
|
|
|
case '-': ret |= FMT_FLAG_DASH; ++fmt; break;
|
|
|
|
case '+': ret |= FMT_FLAG_PLUS; ++fmt; break;
|
|
|
|
case '#': ret |= FMT_FLAG_HASH; ++fmt; break;
|
|
|
|
case '0': ret |= FMT_FLAG_ZERO; ++fmt; break;
|
|
|
|
case ' ': ret |= FMT_FLAG_SPACE; ++fmt; break;
|
|
|
|
default: goto retflags;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
retflags:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2015-07-11 15:07:52 +00:00
|
|
|
inline Size read_digits(const char *&fmt, char *buf) {
|
2015-07-05 22:59:36 +00:00
|
|
|
Size ret = 0;
|
2015-07-02 18:02:59 +00:00
|
|
|
for (; isdigit(*fmt); ++ret)
|
|
|
|
*buf++ = *fmt++;
|
|
|
|
*buf = '\0';
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2015-07-03 20:13:54 +00:00
|
|
|
/* 0 .. not allowed
|
|
|
|
* 1 .. floating point
|
|
|
|
* 2 .. character
|
|
|
|
* 3 .. binary
|
|
|
|
* 4 .. octal
|
|
|
|
* 5 .. decimal
|
|
|
|
* 6 .. hexadecimal
|
|
|
|
* 7 .. string
|
2015-07-04 17:27:09 +00:00
|
|
|
* 8 .. custom object
|
2015-07-03 20:13:54 +00:00
|
|
|
*/
|
2015-07-05 22:59:36 +00:00
|
|
|
static constexpr const byte fmt_specs[] = {
|
2015-07-02 19:58:04 +00:00
|
|
|
/* uppercase spec set */
|
2015-07-04 17:27:09 +00:00
|
|
|
1, 3, 8, 8, /* A B C D */
|
|
|
|
1, 1, 1, 8, /* E F G H */
|
|
|
|
8, 8, 8, 8, /* I J K L */
|
|
|
|
8, 8, 8, 8, /* M N O P */
|
|
|
|
8, 8, 8, 8, /* Q R S T */
|
|
|
|
8, 8, 8, 6, /* U V W X */
|
|
|
|
8, 8, /* Y Z */
|
2015-07-02 19:58:04 +00:00
|
|
|
|
|
|
|
/* ascii filler */
|
|
|
|
0, 0, 0, 0, 0, 0,
|
|
|
|
|
|
|
|
/* lowercase spec set */
|
2015-07-03 20:13:54 +00:00
|
|
|
1, 3, 2, 5, /* a b c d */
|
2015-07-04 17:27:09 +00:00
|
|
|
1, 1, 1, 8, /* e f g h */
|
|
|
|
8, 8, 8, 8, /* i j k l */
|
|
|
|
8, 8, 4, 8, /* m n o p */
|
|
|
|
8, 8, 7, 8, /* q r s t */
|
|
|
|
8, 8, 8, 6, /* u v w x */
|
|
|
|
8, 8, /* y z */
|
2015-07-02 19:58:04 +00:00
|
|
|
|
|
|
|
/* ascii filler */
|
|
|
|
0, 0, 0, 0, 0
|
|
|
|
};
|
2015-07-03 20:13:54 +00:00
|
|
|
|
|
|
|
static constexpr const int fmt_bases[] = {
|
|
|
|
0, 0, 0, 2, 8, 10, 16, 0
|
|
|
|
};
|
|
|
|
|
|
|
|
static constexpr const char fmt_digits[2][16] = {
|
|
|
|
{
|
|
|
|
'0', '1', '2', '3', '4', '5', '6', '7',
|
|
|
|
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
'0', '1', '2', '3', '4', '5', '6', '7',
|
|
|
|
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
static constexpr const char *fmt_intpfx[2][4] = {
|
|
|
|
{ "0B", "0", "", "0X" },
|
|
|
|
{ "0b", "0", "", "0x" }
|
|
|
|
};
|
2015-07-02 18:02:59 +00:00
|
|
|
|
2015-07-04 18:19:49 +00:00
|
|
|
/* retrieve width/precision */
|
|
|
|
template<typename T>
|
2015-07-05 22:59:36 +00:00
|
|
|
bool convert_arg_param(const T &val, int ¶m, EnableIf<
|
|
|
|
IsIntegral<T>::value, bool
|
2015-07-04 18:19:49 +00:00
|
|
|
> = true) {
|
|
|
|
param = int(val);
|
|
|
|
return true;
|
|
|
|
}
|
2015-07-02 18:02:59 +00:00
|
|
|
|
2015-07-04 18:19:49 +00:00
|
|
|
template<typename T>
|
2015-07-05 22:59:36 +00:00
|
|
|
bool convert_arg_param(const T &, int &, EnableIf<
|
|
|
|
!IsIntegral<T>::value, bool
|
2015-07-04 18:19:49 +00:00
|
|
|
> = true) {
|
|
|
|
assert(false && "invalid argument for width/precision");
|
|
|
|
return false;
|
|
|
|
}
|
2015-07-04 00:04:09 +00:00
|
|
|
|
2015-07-04 18:19:49 +00:00
|
|
|
template<typename T>
|
2015-07-05 22:59:36 +00:00
|
|
|
bool get_arg_param(Size idx, int ¶m, const T &val) {
|
2015-07-04 18:19:49 +00:00
|
|
|
if (idx) {
|
|
|
|
assert(false && "not enough format args");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return convert_arg_param(val, param);
|
|
|
|
}
|
|
|
|
template<typename T, typename ...A>
|
2015-07-05 22:59:36 +00:00
|
|
|
bool get_arg_param(Size idx, int ¶m, const T &val,
|
2015-07-04 18:19:49 +00:00
|
|
|
const A &...args) {
|
|
|
|
if (idx) return get_arg_param(idx - 1, param, args...);
|
|
|
|
return convert_arg_param(val, param);
|
|
|
|
}
|
|
|
|
}
|
2015-07-04 00:04:09 +00:00
|
|
|
|
2015-07-04 18:19:49 +00:00
|
|
|
struct FormatSpec {
|
2015-07-05 20:12:41 +00:00
|
|
|
FormatSpec(): p_nested_escape(false), p_fmt(nullptr) {}
|
2015-07-04 18:19:49 +00:00
|
|
|
FormatSpec(const char *fmt, bool escape = false):
|
|
|
|
p_nested_escape(escape), p_fmt(fmt) {}
|
2015-07-04 00:14:33 +00:00
|
|
|
|
2015-07-02 18:02:59 +00:00
|
|
|
template<typename R>
|
2015-07-05 22:59:36 +00:00
|
|
|
bool read_until_spec(R &writer, Size *wret) {
|
|
|
|
Size written = 0;
|
2015-07-02 18:02:59 +00:00
|
|
|
if (!p_fmt) return false;
|
|
|
|
while (*p_fmt) {
|
|
|
|
if (*p_fmt == '%') {
|
|
|
|
++p_fmt;
|
|
|
|
if (*p_fmt == '%') goto plain;
|
|
|
|
bool r = read_spec();
|
|
|
|
if (wret) *wret = written;
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
plain:
|
|
|
|
++written;
|
|
|
|
writer.put(*p_fmt++);
|
|
|
|
}
|
2015-07-03 17:21:05 +00:00
|
|
|
if (wret) *wret = written;
|
2015-07-02 18:02:59 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
template<typename R>
|
2015-07-05 22:59:36 +00:00
|
|
|
Size write_spaces(R &writer, Size n, bool left, char c = ' ') const {
|
2015-07-04 18:19:49 +00:00
|
|
|
if (left == bool(p_flags & FMT_FLAG_DASH)) return 0;
|
|
|
|
int r = p_width - int(n);
|
|
|
|
for (int w = p_width - int(n); --w >= 0; writer.put(c));
|
2015-07-03 17:21:05 +00:00
|
|
|
if (r < 0) return 0;
|
|
|
|
return r;
|
2015-07-02 18:02:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
const char *rest() const {
|
|
|
|
return p_fmt;
|
|
|
|
}
|
|
|
|
|
2015-07-05 22:59:36 +00:00
|
|
|
void build_spec(char *buf, const char *spec, Size specn) {
|
2015-07-03 17:21:05 +00:00
|
|
|
*buf++ = '%';
|
2015-07-04 18:19:49 +00:00
|
|
|
if (p_flags & FMT_FLAG_DASH ) *buf++ = '-';
|
|
|
|
if (p_flags & FMT_FLAG_ZERO ) *buf++ = '0';
|
|
|
|
if (p_flags & FMT_FLAG_SPACE) *buf++ = ' ';
|
|
|
|
if (p_flags & FMT_FLAG_PLUS ) *buf++ = '+';
|
|
|
|
if (p_flags & FMT_FLAG_HASH ) *buf++ = '#';
|
2015-07-03 17:21:05 +00:00
|
|
|
memcpy(buf, "*.*", 3);
|
|
|
|
memcpy(buf + 3, spec, specn);
|
|
|
|
*(buf += specn + 3) = '\0';
|
|
|
|
}
|
|
|
|
|
2015-07-04 18:19:49 +00:00
|
|
|
int width() const { return p_width; }
|
|
|
|
int precision() const { return p_precision; }
|
|
|
|
|
|
|
|
bool has_width() const { return p_has_width; }
|
|
|
|
bool has_precision() const { return p_has_precision; }
|
|
|
|
|
|
|
|
bool arg_width() const { return p_arg_width; }
|
|
|
|
bool arg_precision() const { return p_arg_precision; }
|
|
|
|
|
|
|
|
template<typename ...A>
|
2015-07-05 22:59:36 +00:00
|
|
|
bool set_width(Size idx, const A &...args) {
|
|
|
|
return detail::get_arg_param(idx, p_width, args...);
|
2015-07-04 18:19:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
template<typename ...A>
|
2015-07-05 22:59:36 +00:00
|
|
|
bool set_precision(Size idx, const A &...args) {
|
|
|
|
return detail::get_arg_param(idx, p_precision, args...);
|
2015-07-04 18:19:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int flags() const { return p_flags; }
|
|
|
|
|
|
|
|
char spec() const { return p_spec; }
|
|
|
|
|
2015-07-05 22:59:36 +00:00
|
|
|
byte index() const { return p_index; }
|
2015-07-04 18:19:49 +00:00
|
|
|
|
|
|
|
const char *nested() const { return p_nested; }
|
2015-07-05 22:59:36 +00:00
|
|
|
Size nested_len() const { return p_nested_len; }
|
2015-07-04 18:19:49 +00:00
|
|
|
|
|
|
|
const char *nested_sep() const { return p_nested_sep; }
|
2015-07-05 22:59:36 +00:00
|
|
|
Size nested_sep_len() const { return p_nested_sep_len; }
|
2015-07-04 18:19:49 +00:00
|
|
|
|
|
|
|
bool is_nested() const { return p_is_nested; }
|
|
|
|
bool nested_escape() const { return p_nested_escape; }
|
|
|
|
|
|
|
|
protected:
|
|
|
|
const char *p_nested = nullptr;
|
2015-07-05 22:59:36 +00:00
|
|
|
Size p_nested_len = 0;
|
2015-07-04 18:19:49 +00:00
|
|
|
|
|
|
|
const char *p_nested_sep = nullptr;
|
2015-07-05 22:59:36 +00:00
|
|
|
Size p_nested_sep_len = 0;
|
2015-07-04 18:19:49 +00:00
|
|
|
|
|
|
|
int p_flags = 0;
|
|
|
|
|
|
|
|
int p_width = 0;
|
|
|
|
int p_precision = 0;
|
|
|
|
|
|
|
|
bool p_has_width = false;
|
|
|
|
bool p_has_precision = false;
|
|
|
|
|
|
|
|
bool p_arg_width = false;
|
|
|
|
bool p_arg_precision = false;
|
|
|
|
|
|
|
|
char p_spec = '\0';
|
|
|
|
|
2015-07-05 22:59:36 +00:00
|
|
|
byte p_index = 0;
|
2015-07-04 18:19:49 +00:00
|
|
|
|
|
|
|
bool p_is_nested = false;
|
|
|
|
bool p_nested_escape = false;
|
2015-07-02 19:58:04 +00:00
|
|
|
|
2015-07-04 00:04:09 +00:00
|
|
|
bool read_until_dummy() {
|
|
|
|
while (*p_fmt) {
|
|
|
|
if (*p_fmt == '%') {
|
|
|
|
++p_fmt;
|
|
|
|
if (*p_fmt == '%') goto plain;
|
|
|
|
return read_spec();
|
|
|
|
}
|
|
|
|
plain:
|
|
|
|
++p_fmt;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool read_spec_range() {
|
2015-07-09 23:49:07 +00:00
|
|
|
int sflags = p_flags;
|
|
|
|
p_nested_escape = !(sflags & FMT_FLAG_DASH);
|
2015-07-04 16:40:07 +00:00
|
|
|
++p_fmt;
|
2015-07-04 00:04:09 +00:00
|
|
|
const char *begin_inner = p_fmt;
|
2015-07-04 00:14:33 +00:00
|
|
|
if (!read_until_dummy()) {
|
2015-07-04 18:19:49 +00:00
|
|
|
p_is_nested = false;
|
2015-07-04 00:14:33 +00:00
|
|
|
return false;
|
|
|
|
}
|
2015-07-04 15:06:04 +00:00
|
|
|
/* skip to the last spec in case multiple specs are present */
|
|
|
|
const char *curfmt = p_fmt;
|
|
|
|
while (read_until_dummy()) {
|
|
|
|
curfmt = p_fmt;
|
|
|
|
}
|
|
|
|
p_fmt = curfmt;
|
2015-07-09 23:49:07 +00:00
|
|
|
p_flags = sflags;
|
2015-07-04 00:04:09 +00:00
|
|
|
/* find delimiter or ending */
|
|
|
|
const char *begin_delim = p_fmt;
|
|
|
|
const char *p = strchr(begin_delim, '%');
|
|
|
|
for (; p; p = strchr(p, '%')) {
|
|
|
|
++p;
|
2015-07-04 15:06:04 +00:00
|
|
|
/* escape, skip */
|
2015-07-04 00:04:09 +00:00
|
|
|
if (*p == '%') {
|
|
|
|
++p;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
/* found end, in that case delimiter is after spec */
|
|
|
|
if (*p == ')') {
|
2015-07-04 18:19:49 +00:00
|
|
|
p_nested = begin_inner;
|
|
|
|
p_nested_len = begin_delim - begin_inner;
|
|
|
|
p_nested_sep = begin_delim;
|
|
|
|
p_nested_sep_len = p - p_nested_sep - 1;
|
2015-07-04 00:04:09 +00:00
|
|
|
p_fmt = ++p;
|
2015-07-04 18:19:49 +00:00
|
|
|
p_is_nested = true;
|
2015-07-04 00:04:09 +00:00
|
|
|
return true;
|
|
|
|
}
|
2015-07-04 15:06:04 +00:00
|
|
|
/* found actual delimiter start... */
|
2015-07-04 00:04:09 +00:00
|
|
|
if (*p == '|') {
|
2015-07-04 18:19:49 +00:00
|
|
|
p_nested = begin_inner;
|
|
|
|
p_nested_len = p - begin_inner - 1;
|
2015-07-04 00:04:09 +00:00
|
|
|
++p;
|
2015-07-04 18:19:49 +00:00
|
|
|
p_nested_sep = p;
|
2015-07-04 00:04:09 +00:00
|
|
|
for (p = strchr(p, '%'); p; p = strchr(p, '%')) {
|
|
|
|
++p;
|
|
|
|
if (*p == ')') {
|
2015-07-04 18:19:49 +00:00
|
|
|
p_nested_sep_len = p - p_nested_sep - 1;
|
2015-07-04 00:04:09 +00:00
|
|
|
p_fmt = ++p;
|
2015-07-04 18:19:49 +00:00
|
|
|
p_is_nested = true;
|
2015-07-04 00:04:09 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
2015-07-04 18:19:49 +00:00
|
|
|
p_is_nested = false;
|
2015-07-04 00:04:09 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
2015-07-04 18:19:49 +00:00
|
|
|
p_is_nested = false;
|
2015-07-04 00:04:09 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-07-02 18:02:59 +00:00
|
|
|
bool read_spec() {
|
2015-07-05 22:59:36 +00:00
|
|
|
Size ndig = detail::read_digits(p_fmt, p_buf);
|
2015-07-02 18:02:59 +00:00
|
|
|
|
|
|
|
bool havepos = false;
|
2015-07-04 18:19:49 +00:00
|
|
|
p_index = 0;
|
2015-07-02 18:02:59 +00:00
|
|
|
/* parse index */
|
|
|
|
if (*p_fmt == '$') {
|
|
|
|
if (ndig <= 0) return false; /* no pos given */
|
|
|
|
int idx = atoi(p_buf);
|
|
|
|
if (idx <= 0 || idx > 255) return false; /* bad index */
|
2015-07-05 22:59:36 +00:00
|
|
|
p_index = byte(idx);
|
2015-07-02 18:02:59 +00:00
|
|
|
++p_fmt;
|
|
|
|
havepos = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* parse flags */
|
2015-07-04 18:19:49 +00:00
|
|
|
p_flags = 0;
|
2015-07-05 22:59:36 +00:00
|
|
|
Size skipd = 0;
|
2015-07-02 18:02:59 +00:00
|
|
|
if (havepos || !ndig) {
|
2015-07-05 22:59:36 +00:00
|
|
|
p_flags = detail::parse_fmt_flags(p_fmt, 0);
|
2015-07-02 18:02:59 +00:00
|
|
|
} else {
|
2015-07-05 22:59:36 +00:00
|
|
|
for (Size i = 0; i < ndig; ++i) {
|
2015-07-02 18:02:59 +00:00
|
|
|
if (p_buf[i] != '0') break;
|
|
|
|
++skipd;
|
|
|
|
}
|
2015-07-04 18:19:49 +00:00
|
|
|
if (skipd) p_flags = FMT_FLAG_ZERO;
|
2015-07-02 18:02:59 +00:00
|
|
|
if (skipd == ndig)
|
2015-07-05 22:59:36 +00:00
|
|
|
p_flags = detail::parse_fmt_flags(p_fmt, p_flags);
|
2015-07-02 01:00:05 +00:00
|
|
|
}
|
|
|
|
|
2015-07-09 23:24:47 +00:00
|
|
|
/* range/array formatting */
|
|
|
|
if ((*p_fmt == '(') && (havepos || !(ndig - skipd))) {
|
|
|
|
return read_spec_range();
|
|
|
|
}
|
|
|
|
|
2015-07-02 18:02:59 +00:00
|
|
|
/* parse width */
|
2015-07-04 18:19:49 +00:00
|
|
|
p_width = 0;
|
|
|
|
p_has_width = false;
|
|
|
|
p_arg_width = false;
|
2015-07-02 18:02:59 +00:00
|
|
|
if (!havepos && ndig && (ndig - skipd)) {
|
2015-07-04 18:19:49 +00:00
|
|
|
p_width = atoi(p_buf + skipd);
|
|
|
|
p_has_width = true;
|
2015-07-05 22:59:36 +00:00
|
|
|
} else if (detail::read_digits(p_fmt, p_buf)) {
|
2015-07-04 18:19:49 +00:00
|
|
|
p_width = atoi(p_buf);
|
|
|
|
p_has_width = true;
|
2015-07-02 18:02:59 +00:00
|
|
|
} else if (*p_fmt == '*') {
|
2015-07-04 18:19:49 +00:00
|
|
|
p_arg_width = p_has_width = true;
|
2015-07-02 18:02:59 +00:00
|
|
|
++p_fmt;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* parse precision */
|
2015-07-04 18:19:49 +00:00
|
|
|
p_precision = 0;
|
|
|
|
p_has_precision = false;
|
|
|
|
p_arg_precision = false;
|
2015-07-02 18:02:59 +00:00
|
|
|
if (*p_fmt != '.') goto fmtchar;
|
|
|
|
++p_fmt;
|
|
|
|
|
2015-07-05 22:59:36 +00:00
|
|
|
if (detail::read_digits(p_fmt, p_buf)) {
|
2015-07-04 18:19:49 +00:00
|
|
|
p_precision = atoi(p_buf);
|
|
|
|
p_has_precision = true;
|
2015-07-02 18:02:59 +00:00
|
|
|
} else if (*p_fmt == '*') {
|
2015-07-04 18:19:49 +00:00
|
|
|
p_arg_precision = p_has_precision = true;
|
2015-07-02 18:02:59 +00:00
|
|
|
++p_fmt;
|
|
|
|
} else return false;
|
|
|
|
|
|
|
|
fmtchar:
|
2015-07-04 18:19:49 +00:00
|
|
|
p_spec = *p_fmt++;
|
2015-07-02 19:58:04 +00:00
|
|
|
/* make sure we're testing on a signed byte - our mapping only
|
|
|
|
* tests values up to 127 */
|
2015-07-05 22:59:36 +00:00
|
|
|
sbyte sp = p_spec;
|
|
|
|
return (sp >= 65) && (detail::fmt_specs[sp - 65] != 0);
|
2015-07-02 18:02:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
const char *p_fmt;
|
|
|
|
char p_buf[32];
|
|
|
|
};
|
|
|
|
|
|
|
|
namespace detail {
|
2015-07-03 00:59:34 +00:00
|
|
|
template<typename R, typename T>
|
2015-07-11 15:07:52 +00:00
|
|
|
inline Ptrdiff write_u(R &writer, const FormatSpec *fl, bool neg, T val) {
|
2015-07-01 17:51:39 +00:00
|
|
|
char buf[20];
|
2015-07-05 22:59:36 +00:00
|
|
|
Ptrdiff r = 0;
|
|
|
|
Size n = 0;
|
2015-07-03 20:13:54 +00:00
|
|
|
|
2015-07-04 18:19:49 +00:00
|
|
|
char spec = fl->spec();
|
2015-07-03 20:13:54 +00:00
|
|
|
if (spec == 's') spec = 'd';
|
2015-07-05 22:59:36 +00:00
|
|
|
byte specn = detail::fmt_specs[spec - 65];
|
2015-07-04 17:27:09 +00:00
|
|
|
if (specn <= 2 || specn > 7) {
|
2015-07-03 20:13:54 +00:00
|
|
|
assert(false && "cannot format integers with the given spec");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2015-07-05 22:59:36 +00:00
|
|
|
int base = detail::fmt_bases[specn];
|
2015-07-04 01:18:21 +00:00
|
|
|
if (!val) buf[n++] = '0';
|
2015-07-03 20:13:54 +00:00
|
|
|
for (; val; val /= base)
|
2015-07-05 22:59:36 +00:00
|
|
|
buf[n++] = detail::fmt_digits[spec >= 'a'][val % base];
|
2015-07-03 20:13:54 +00:00
|
|
|
r = n;
|
|
|
|
|
2015-07-04 18:19:49 +00:00
|
|
|
int flags = fl->flags();
|
|
|
|
bool lsgn = flags & FMT_FLAG_PLUS;
|
|
|
|
bool lsp = flags & FMT_FLAG_SPACE;
|
|
|
|
bool zero = flags & FMT_FLAG_ZERO;
|
2015-07-03 20:13:54 +00:00
|
|
|
bool sign = neg + lsgn + lsp;
|
|
|
|
r += sign;
|
|
|
|
|
|
|
|
const char *pfx = nullptr;
|
|
|
|
int pfxlen = 0;
|
2015-07-04 18:19:49 +00:00
|
|
|
if (flags & FMT_FLAG_HASH && spec != 'd') {
|
2015-07-05 22:59:36 +00:00
|
|
|
pfx = detail::fmt_intpfx[spec >= 'a'][specn - 3];
|
2015-07-03 20:19:50 +00:00
|
|
|
pfxlen = !!pfx[1] + 1;
|
2015-07-03 20:13:54 +00:00
|
|
|
r += pfxlen;
|
|
|
|
}
|
|
|
|
|
2015-07-04 18:19:49 +00:00
|
|
|
if (!zero)
|
|
|
|
r += fl->write_spaces(writer, n + pfxlen + sign, true, ' ');
|
2015-07-02 18:02:59 +00:00
|
|
|
if (sign) writer.put(neg ? '-' : *((" \0+") + lsgn * 2));
|
2015-07-03 20:13:54 +00:00
|
|
|
writer.put_n(pfx, pfxlen);
|
2015-07-04 18:19:49 +00:00
|
|
|
if (zero)
|
|
|
|
r += fl->write_spaces(writer, n + pfxlen + sign, true, '0');
|
2015-07-03 20:13:54 +00:00
|
|
|
|
2015-07-01 17:51:39 +00:00
|
|
|
for (int i = int(n - 1); i >= 0; --i) {
|
|
|
|
writer.put(buf[i]);
|
|
|
|
}
|
2015-07-04 18:19:49 +00:00
|
|
|
r += fl->write_spaces(writer, n + sign + pfxlen, false);
|
2015-07-03 20:13:54 +00:00
|
|
|
return r;
|
2015-07-01 17:51:39 +00:00
|
|
|
}
|
|
|
|
|
2015-07-04 01:18:21 +00:00
|
|
|
template<typename R, typename ...A>
|
2015-07-05 22:59:36 +00:00
|
|
|
static Ptrdiff format_impl(R &writer, Size &fmtn, bool escape,
|
|
|
|
const char *fmt, const A &...args);
|
2015-07-04 01:18:21 +00:00
|
|
|
|
2015-07-05 22:59:36 +00:00
|
|
|
template<typename T, typename = RangeOf<T>>
|
|
|
|
static True test_fmt_range(int);
|
2015-07-04 19:43:03 +00:00
|
|
|
template<typename>
|
2015-07-05 22:59:36 +00:00
|
|
|
static False test_fmt_range(...);
|
2015-07-04 01:18:21 +00:00
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
using FmtRangeTest = decltype(test_fmt_range<T>(0));
|
|
|
|
|
2015-07-04 14:23:54 +00:00
|
|
|
template<typename R, typename T>
|
2015-07-11 15:07:52 +00:00
|
|
|
inline Ptrdiff format_ritem(R &writer, Size &fmtn, bool esc, bool,
|
|
|
|
const char *fmt, const T &item) {
|
2015-07-04 16:40:07 +00:00
|
|
|
return format_impl(writer, fmtn, esc, fmt, item);
|
2015-07-04 13:52:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
template<typename R, typename T, typename U>
|
2015-07-11 15:07:52 +00:00
|
|
|
inline Ptrdiff format_ritem(R &writer, Size &fmtn, bool esc,
|
|
|
|
bool expandval, const char *fmt,
|
|
|
|
const Pair<T, U> &pair) {
|
2015-07-09 23:49:07 +00:00
|
|
|
if (expandval) {
|
|
|
|
return format_impl(writer, fmtn, esc, fmt, pair.first,
|
|
|
|
pair.second);
|
|
|
|
}
|
|
|
|
return format_impl(writer, fmtn, esc, fmt, pair);
|
2015-07-04 13:52:02 +00:00
|
|
|
}
|
|
|
|
|
2015-07-04 01:18:21 +00:00
|
|
|
template<typename R, typename T>
|
2015-07-11 15:07:52 +00:00
|
|
|
inline Ptrdiff write_range(R &writer, const FormatSpec *fl,
|
|
|
|
bool escape, bool expandval,
|
|
|
|
const char *sep, Size seplen,
|
|
|
|
const T &val,
|
|
|
|
EnableIf<FmtRangeTest<T>::value, bool>
|
|
|
|
= true) {
|
2015-07-04 01:30:46 +00:00
|
|
|
auto range = octa::iter(val);
|
2015-07-04 01:18:21 +00:00
|
|
|
if (range.empty()) return 0;
|
2015-07-05 22:59:36 +00:00
|
|
|
Ptrdiff ret = 0;
|
|
|
|
Size fmtn = 0;
|
2015-07-04 01:18:21 +00:00
|
|
|
/* test first item */
|
2015-07-09 23:49:07 +00:00
|
|
|
Ptrdiff fret = format_ritem(writer, fmtn, escape, expandval,
|
|
|
|
fl->rest(), range.front());
|
2015-07-04 01:18:21 +00:00
|
|
|
if (fret < 0) return fret;
|
|
|
|
ret += fret;
|
|
|
|
range.pop_front();
|
|
|
|
/* write the rest (if any) */
|
|
|
|
for (; !range.empty(); range.pop_front()) {
|
|
|
|
auto v = writer.put_n(sep, seplen);
|
|
|
|
if (v != seplen)
|
|
|
|
return -1;
|
|
|
|
ret += seplen;
|
2015-07-09 23:49:07 +00:00
|
|
|
fret = format_ritem(writer, fmtn, escape, expandval,
|
|
|
|
fl->rest(), range.front());
|
2015-07-04 01:18:21 +00:00
|
|
|
if (fret < 0) return fret;
|
|
|
|
ret += fret;
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
template<typename R, typename T>
|
2015-07-11 15:07:52 +00:00
|
|
|
inline Ptrdiff write_range(R &, const FormatSpec *, bool, bool,
|
|
|
|
const char *, Size, const T &,
|
|
|
|
EnableIf<!FmtRangeTest<T>::value, bool>
|
|
|
|
= true) {
|
2015-07-04 01:18:21 +00:00
|
|
|
assert(false && "invalid value for ranged format");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2015-07-04 13:52:02 +00:00
|
|
|
template<typename T,
|
2015-07-05 22:59:36 +00:00
|
|
|
typename = decltype(octa::to_string(declval<T>()))
|
|
|
|
> static True test_fmt_tostr(int);
|
|
|
|
template<typename> static False test_fmt_tostr(...);
|
2015-07-04 13:52:02 +00:00
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
using FmtTostrTest = decltype(test_fmt_tostr<T>(0));
|
|
|
|
|
2015-07-04 16:40:07 +00:00
|
|
|
/* non-printable escapes up to 0x20 (space) */
|
|
|
|
static constexpr const char *fmt_escapes[] = {
|
|
|
|
"\\0" , "\\x01", "\\x02", "\\x03", "\\x04", "\\x05",
|
|
|
|
"\\x06", "\\a" , "\\b" , "\\t" , "\\n" , "\\v" ,
|
|
|
|
"\\f" , "\\r" , "\\x0E", "\\x0F", "\\x10", "\\x11",
|
|
|
|
"\\x12", "\\x13", "\\x14", "\\x15", "\\x16", "\\x17",
|
|
|
|
"\\x18", "\\x19", "\\x1A", "\\x1B", "\\x1C", "\\x1D",
|
|
|
|
"\\x1E", "\\x1F",
|
|
|
|
/* we want to escape double quotes... */
|
|
|
|
nullptr, nullptr, "\\\"", nullptr, nullptr, nullptr,
|
|
|
|
nullptr, "\\\'"
|
|
|
|
};
|
|
|
|
|
2015-07-11 15:07:52 +00:00
|
|
|
inline const char *escape_fmt_char(char v, char quote) {
|
2015-07-04 16:40:07 +00:00
|
|
|
if ((v >= 0 && v < 0x20) || (v == quote)) {
|
2015-07-05 22:59:36 +00:00
|
|
|
return fmt_escapes[Size(v)];
|
2015-07-04 16:40:07 +00:00
|
|
|
} else if (v == 0x7F) {
|
|
|
|
return "\\x7F";
|
|
|
|
}
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2015-07-11 15:07:52 +00:00
|
|
|
inline String escape_fmt_str(const char *val) {
|
2015-07-05 22:59:36 +00:00
|
|
|
String ret;
|
2015-07-04 16:40:07 +00:00
|
|
|
ret.push('"');
|
|
|
|
while (*val) {
|
|
|
|
const char *esc = escape_fmt_char(*val, '"');
|
|
|
|
if (esc)
|
|
|
|
ret.append(esc);
|
|
|
|
else
|
|
|
|
ret.push(*val);
|
|
|
|
++val;
|
|
|
|
}
|
|
|
|
ret.push('"');
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2015-07-04 17:27:09 +00:00
|
|
|
template<typename R>
|
2015-07-05 22:59:36 +00:00
|
|
|
struct FmtWriteRange: OutputRange<FmtWriteRange<R>, char> {
|
2015-07-04 17:27:09 +00:00
|
|
|
FmtWriteRange() = delete;
|
|
|
|
FmtWriteRange(R &out): p_out(out), p_written(0) {}
|
|
|
|
bool put(char v) {
|
|
|
|
bool ret = p_out.put(v);
|
|
|
|
p_written += ret;
|
|
|
|
return ret;
|
|
|
|
}
|
2015-07-05 22:59:36 +00:00
|
|
|
Size put_n(const char *v, Size n) {
|
|
|
|
Size ret = p_out.put_n(v, n);
|
2015-07-04 17:27:09 +00:00
|
|
|
p_written += ret;
|
|
|
|
return ret;
|
|
|
|
}
|
2015-07-05 22:59:36 +00:00
|
|
|
Size get_written() const { return p_written; }
|
2015-07-04 17:27:09 +00:00
|
|
|
private:
|
|
|
|
R &p_out;
|
2015-07-05 22:59:36 +00:00
|
|
|
Size p_written;
|
2015-07-04 17:27:09 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
template<typename T, typename U>
|
|
|
|
struct FmtTofmtTest {
|
|
|
|
template<typename TT, typename UU,
|
|
|
|
bool (TT::*)(UU &, const FormatSpec &) const
|
|
|
|
> struct Test {};
|
|
|
|
template<typename TT, typename UU>
|
|
|
|
static char test(Test<TT, UU, &TT::template to_format<UU>> *);
|
|
|
|
template<typename, typename> static int test(...);
|
|
|
|
static constexpr bool value = (sizeof(test<T, U>(0)) == sizeof(char));
|
|
|
|
};
|
|
|
|
|
2015-07-05 22:59:36 +00:00
|
|
|
struct WriteSpec: FormatSpec {
|
|
|
|
WriteSpec(): FormatSpec() {}
|
|
|
|
WriteSpec(const char *fmt, bool esc): FormatSpec(fmt, esc) {}
|
2015-07-03 17:21:05 +00:00
|
|
|
|
|
|
|
/* C string */
|
|
|
|
template<typename R>
|
2015-07-05 22:59:36 +00:00
|
|
|
Ptrdiff write(R &writer, bool escape, const char *val, Size n) {
|
2015-07-04 18:19:49 +00:00
|
|
|
if (escape) {
|
2015-07-05 22:59:36 +00:00
|
|
|
String esc = escape_fmt_str(val);
|
2015-07-04 18:19:49 +00:00
|
|
|
return write(writer, false, (const char *)esc.data(),
|
2015-07-04 16:40:07 +00:00
|
|
|
esc.size());
|
2015-07-03 20:13:54 +00:00
|
|
|
}
|
2015-07-04 18:19:49 +00:00
|
|
|
if (this->precision()) n = this->precision();
|
2015-07-05 22:59:36 +00:00
|
|
|
Ptrdiff r = n;
|
2015-07-04 18:19:49 +00:00
|
|
|
r += this->write_spaces(writer, n, true);
|
2015-07-03 17:21:05 +00:00
|
|
|
writer.put_n(val, n);
|
2015-07-04 18:19:49 +00:00
|
|
|
r += this->write_spaces(writer, n, false);
|
2015-07-03 17:21:05 +00:00
|
|
|
return r;
|
2015-07-01 17:51:39 +00:00
|
|
|
}
|
|
|
|
|
2015-07-03 20:50:02 +00:00
|
|
|
template<typename R>
|
2015-07-05 22:59:36 +00:00
|
|
|
Ptrdiff write(R &writer, bool escape, const char *val) {
|
2015-07-04 18:19:49 +00:00
|
|
|
if (this->spec() != 's') {
|
2015-07-04 16:40:07 +00:00
|
|
|
assert(false && "cannot print strings with the given spec");
|
|
|
|
return -1;
|
|
|
|
}
|
2015-07-04 18:19:49 +00:00
|
|
|
return write(writer, escape, val, strlen(val));
|
2015-07-03 20:50:02 +00:00
|
|
|
}
|
|
|
|
|
2015-07-03 17:21:05 +00:00
|
|
|
/* OctaSTD string */
|
|
|
|
template<typename R, typename A>
|
2015-07-05 22:59:36 +00:00
|
|
|
Ptrdiff write(R &writer, bool escape, const AnyString<A> &val) {
|
2015-07-04 18:19:49 +00:00
|
|
|
if (this->spec() != 's') {
|
2015-07-04 16:40:07 +00:00
|
|
|
assert(false && "cannot print strings with the given spec");
|
|
|
|
return -1;
|
|
|
|
}
|
2015-07-04 18:19:49 +00:00
|
|
|
return write(writer, escape, val.data(), val.size());
|
2015-07-01 17:51:39 +00:00
|
|
|
}
|
|
|
|
|
2015-07-03 17:21:05 +00:00
|
|
|
/* character */
|
|
|
|
template<typename R>
|
2015-07-05 22:59:36 +00:00
|
|
|
Ptrdiff write(R &writer, bool escape, char val) {
|
2015-07-04 18:19:49 +00:00
|
|
|
if (this->spec() != 's' && this->spec() != 'c') {
|
2015-07-03 20:13:54 +00:00
|
|
|
assert(false && "cannot print chars with the given spec");
|
|
|
|
return -1;
|
|
|
|
}
|
2015-07-04 18:19:49 +00:00
|
|
|
if (escape) {
|
2015-07-04 16:40:07 +00:00
|
|
|
const char *esc = escape_fmt_char(val, '\'');
|
|
|
|
if (esc) {
|
|
|
|
char buf[6];
|
|
|
|
buf[0] = '\'';
|
2015-07-05 22:59:36 +00:00
|
|
|
Size elen = strlen(esc);
|
2015-07-04 16:40:07 +00:00
|
|
|
memcpy(buf + 1, esc, elen);
|
|
|
|
buf[elen + 1] = '\'';
|
2015-07-04 18:19:49 +00:00
|
|
|
return write(writer, false, (const char *)buf, elen + 2);
|
2015-07-04 16:40:07 +00:00
|
|
|
}
|
|
|
|
}
|
2015-07-05 22:59:36 +00:00
|
|
|
Ptrdiff r = 1 + escape * 2;
|
2015-07-04 18:19:49 +00:00
|
|
|
r += this->write_spaces(writer, 1 + escape * 2, true);
|
|
|
|
if (escape) {
|
2015-07-04 16:40:07 +00:00
|
|
|
writer.put('\'');
|
|
|
|
writer.put(val);
|
|
|
|
writer.put('\'');
|
|
|
|
} else writer.put(val);
|
2015-07-04 18:19:49 +00:00
|
|
|
r += this->write_spaces(writer, 1 + escape * 2, false);
|
2015-07-03 17:21:05 +00:00
|
|
|
return r;
|
2015-07-01 17:51:39 +00:00
|
|
|
}
|
|
|
|
|
2015-07-03 17:21:05 +00:00
|
|
|
/* bool */
|
|
|
|
template<typename R>
|
2015-07-05 22:59:36 +00:00
|
|
|
Ptrdiff write(R &writer, bool, bool val) {
|
2015-07-04 18:19:49 +00:00
|
|
|
if (this->spec() == 's')
|
2015-07-03 20:13:54 +00:00
|
|
|
return write(writer, ("false\0true") + (6 * val));
|
|
|
|
else
|
|
|
|
return write(writer, int(val));
|
2015-07-02 18:02:59 +00:00
|
|
|
}
|
2015-07-01 01:22:42 +00:00
|
|
|
|
2015-07-03 17:21:05 +00:00
|
|
|
/* signed integers */
|
|
|
|
template<typename R, typename T>
|
2015-07-05 22:59:36 +00:00
|
|
|
Ptrdiff write(R &writer, bool, T val, EnableIf<
|
|
|
|
IsIntegral<T>::value && IsSigned<T>::value, bool
|
2015-07-03 17:21:05 +00:00
|
|
|
> = true) {
|
2015-07-05 22:59:36 +00:00
|
|
|
using UT = MakeUnsigned<T>;
|
|
|
|
return detail::write_u(writer, this, val < 0,
|
|
|
|
(val < 0) ? (UT)(-val) : (UT)(val));
|
2015-07-03 17:21:05 +00:00
|
|
|
}
|
2015-07-02 01:00:05 +00:00
|
|
|
|
2015-07-03 17:21:05 +00:00
|
|
|
/* unsigned integers */
|
|
|
|
template<typename R, typename T>
|
2015-07-05 22:59:36 +00:00
|
|
|
Ptrdiff write(R &writer, bool, T val, EnableIf<
|
|
|
|
IsIntegral<T>::value && IsUnsigned<T>::value, bool
|
2015-07-03 17:21:05 +00:00
|
|
|
> = true) {
|
2015-07-05 22:59:36 +00:00
|
|
|
return detail::write_u(writer, this, false, val);
|
2015-07-03 17:21:05 +00:00
|
|
|
}
|
2015-07-02 01:00:05 +00:00
|
|
|
|
2015-07-03 17:21:05 +00:00
|
|
|
template<typename R, typename T,
|
2015-07-05 22:59:36 +00:00
|
|
|
bool Long = IsSame<T, ldouble>::value
|
|
|
|
> Ptrdiff write(R &writer, bool, T val, EnableIf<
|
|
|
|
IsFloatingPoint<T>::value, bool
|
2015-07-03 17:21:05 +00:00
|
|
|
> = true) {
|
|
|
|
char buf[16], rbuf[128];
|
|
|
|
char fmtspec[Long + 1];
|
2015-07-03 20:13:54 +00:00
|
|
|
|
2015-07-04 18:19:49 +00:00
|
|
|
fmtspec[Long] = this->spec();
|
2015-07-05 22:59:36 +00:00
|
|
|
byte specn = detail::fmt_specs[this->spec() - 65];
|
2015-07-03 20:13:54 +00:00
|
|
|
if (specn != 1 && specn != 7) {
|
2015-07-03 20:55:35 +00:00
|
|
|
assert(false && "cannot format floats with the given spec");
|
2015-07-03 20:13:54 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (specn == 7) fmtspec[Long] = 'g';
|
2015-07-03 17:21:05 +00:00
|
|
|
if (Long) fmtspec[0] = 'L';
|
2015-07-03 20:13:54 +00:00
|
|
|
|
2015-07-03 17:21:05 +00:00
|
|
|
this->build_spec(buf, fmtspec, sizeof(fmtspec));
|
2015-07-05 22:59:36 +00:00
|
|
|
Ptrdiff ret = snprintf(rbuf, sizeof(rbuf), buf,
|
2015-07-04 18:19:49 +00:00
|
|
|
this->width(),
|
|
|
|
this->has_precision() ? this->precision() : 6, val);
|
2015-07-03 20:13:54 +00:00
|
|
|
|
2015-07-03 17:21:05 +00:00
|
|
|
char *dbuf = nullptr;
|
2015-07-05 22:59:36 +00:00
|
|
|
if (Size(ret) >= sizeof(rbuf)) {
|
2015-07-03 17:21:05 +00:00
|
|
|
/* this should typically never happen */
|
|
|
|
dbuf = (char *)malloc(ret + 1);
|
2015-07-04 18:19:49 +00:00
|
|
|
ret = snprintf(dbuf, ret + 1, buf, this->width(),
|
|
|
|
this->has_precision() ? this->precision() : 6, val);
|
2015-07-03 17:21:05 +00:00
|
|
|
writer.put_n(dbuf, ret);
|
|
|
|
free(dbuf);
|
|
|
|
} else writer.put_n(rbuf, ret);
|
|
|
|
return ret;
|
|
|
|
}
|
2015-07-02 01:00:05 +00:00
|
|
|
|
2015-07-03 20:19:50 +00:00
|
|
|
/* pointer value */
|
|
|
|
template<typename R, typename T>
|
2015-07-05 22:59:36 +00:00
|
|
|
Ptrdiff write(R &writer, bool, T *val) {
|
2015-07-04 18:19:49 +00:00
|
|
|
if (this->p_spec == 's') {
|
|
|
|
this->p_spec = 'x';
|
|
|
|
this->p_flags |= FMT_FLAG_HASH;
|
2015-07-03 20:19:50 +00:00
|
|
|
}
|
2015-07-05 22:59:36 +00:00
|
|
|
return write(writer, false, Size(val));
|
2015-07-03 20:19:50 +00:00
|
|
|
}
|
|
|
|
|
2015-07-03 17:21:05 +00:00
|
|
|
/* generic value */
|
|
|
|
template<typename R, typename T>
|
2015-07-05 22:59:36 +00:00
|
|
|
Ptrdiff write(R &writer, bool, const T &val, EnableIf<
|
|
|
|
!IsArithmetic<T>::value && FmtTostrTest<T>::value &&
|
2015-07-04 17:27:09 +00:00
|
|
|
!FmtTofmtTest<T, FmtWriteRange<R>>::value, bool
|
2015-07-03 17:21:05 +00:00
|
|
|
> = true) {
|
2015-07-04 18:19:49 +00:00
|
|
|
if (this->spec() != 's') {
|
2015-07-03 20:55:35 +00:00
|
|
|
assert(false && "custom objects need '%s' format");
|
|
|
|
return -1;
|
|
|
|
}
|
2015-07-04 18:19:49 +00:00
|
|
|
return write(writer, false, octa::to_string(val));
|
2015-07-02 18:02:59 +00:00
|
|
|
}
|
2015-07-02 01:00:05 +00:00
|
|
|
|
2015-07-04 17:27:09 +00:00
|
|
|
/* custom format case */
|
|
|
|
template<typename R, typename T>
|
2015-07-05 22:59:36 +00:00
|
|
|
Ptrdiff write(R &writer, bool, const T &val,
|
|
|
|
EnableIf<FmtTofmtTest<T, FmtWriteRange<R>>::value, bool
|
2015-07-04 17:27:09 +00:00
|
|
|
> = true) {
|
|
|
|
FmtWriteRange<R> sink(writer);
|
|
|
|
if (!val.to_format(sink, *this)) return -1;
|
|
|
|
return sink.get_written();
|
|
|
|
}
|
|
|
|
|
2015-07-04 13:52:02 +00:00
|
|
|
/* generic failure case */
|
|
|
|
template<typename R, typename T>
|
2015-07-05 22:59:36 +00:00
|
|
|
Ptrdiff write(R &, bool, const T &, EnableIf<
|
|
|
|
!IsArithmetic<T>::value && !FmtTostrTest<T>::value, bool
|
2015-07-04 13:52:02 +00:00
|
|
|
> = true) {
|
|
|
|
assert(false && "value cannot be formatted");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2015-07-03 17:21:05 +00:00
|
|
|
/* actual writer */
|
|
|
|
template<typename R, typename T>
|
2015-07-05 22:59:36 +00:00
|
|
|
Ptrdiff write_arg(R &writer, Size idx, const T &val) {
|
2015-07-03 17:21:05 +00:00
|
|
|
if (idx) {
|
|
|
|
assert(false && "not enough format args");
|
|
|
|
return -1;
|
|
|
|
}
|
2015-07-04 18:19:49 +00:00
|
|
|
return write(writer, this->p_nested_escape, val);
|
2015-07-03 17:21:05 +00:00
|
|
|
}
|
2015-07-02 01:00:05 +00:00
|
|
|
|
2015-07-03 17:21:05 +00:00
|
|
|
template<typename R, typename T, typename ...A>
|
2015-07-05 22:59:36 +00:00
|
|
|
Ptrdiff write_arg(R &writer, Size idx, const T &val,
|
|
|
|
const A &...args) {
|
2015-07-03 17:21:05 +00:00
|
|
|
if (idx) return write_arg(writer, idx - 1, args...);
|
2015-07-04 18:19:49 +00:00
|
|
|
return write(writer, this->p_nested_escape, val);
|
2015-07-03 17:21:05 +00:00
|
|
|
}
|
2015-07-04 01:18:21 +00:00
|
|
|
|
|
|
|
/* range writer */
|
|
|
|
template<typename R, typename T>
|
2015-07-09 23:49:07 +00:00
|
|
|
Ptrdiff write_range(R &writer, Size idx, bool expandval,
|
|
|
|
const char *sep, Size seplen, const T &val) {
|
2015-07-04 01:18:21 +00:00
|
|
|
if (idx) {
|
|
|
|
assert(false && "not enough format args");
|
|
|
|
return -1;
|
|
|
|
}
|
2015-07-05 22:59:36 +00:00
|
|
|
return detail::write_range(writer, this, this->p_nested_escape,
|
2015-07-09 23:49:07 +00:00
|
|
|
expandval, sep, seplen, val);
|
2015-07-04 01:18:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
template<typename R, typename T, typename ...A>
|
2015-07-09 23:49:07 +00:00
|
|
|
Ptrdiff write_range(R &writer, Size idx, bool expandval,
|
|
|
|
const char *sep, Size seplen, const T &val,
|
|
|
|
const A &...args) {
|
|
|
|
if (idx) {
|
|
|
|
return write_range(writer, idx - 1, expandval, sep,
|
|
|
|
seplen, args...);
|
|
|
|
}
|
2015-07-05 22:59:36 +00:00
|
|
|
return detail::write_range(writer, this,
|
2015-07-09 23:49:07 +00:00
|
|
|
this->p_nested_escape, expandval, sep, seplen, val);
|
2015-07-04 01:18:21 +00:00
|
|
|
}
|
2015-07-03 17:21:05 +00:00
|
|
|
};
|
2015-07-03 20:46:06 +00:00
|
|
|
|
2015-07-04 00:18:14 +00:00
|
|
|
template<typename R, typename ...A>
|
2015-07-11 15:07:52 +00:00
|
|
|
inline Ptrdiff format_impl(R &writer, Size &fmtn, bool escape,
|
|
|
|
const char *fmt, const A &...args) {
|
2015-07-05 22:59:36 +00:00
|
|
|
Size argidx = 1, retn = 0, twr = 0;
|
|
|
|
Ptrdiff written = 0;
|
|
|
|
detail::WriteSpec spec(fmt, escape);
|
2015-07-04 00:18:14 +00:00
|
|
|
while (spec.read_until_spec(writer, &twr)) {
|
|
|
|
written += twr;
|
2015-07-05 22:59:36 +00:00
|
|
|
Size argpos = spec.index();
|
2015-07-04 18:19:49 +00:00
|
|
|
if (spec.is_nested()) {
|
2015-07-04 01:18:21 +00:00
|
|
|
if (!argpos) argpos = argidx++;
|
|
|
|
/* FIXME: figure out a better way */
|
|
|
|
char new_fmt[256];
|
2015-07-04 18:19:49 +00:00
|
|
|
memcpy(new_fmt, spec.nested(), spec.nested_len());
|
|
|
|
new_fmt[spec.nested_len()] = '\0';
|
2015-07-05 22:59:36 +00:00
|
|
|
detail::WriteSpec nspec(new_fmt, spec.nested_escape());
|
|
|
|
Ptrdiff sw = nspec.write_range(writer, argpos - 1,
|
2015-07-09 23:49:07 +00:00
|
|
|
(spec.flags() & FMT_FLAG_HASH),
|
2015-07-04 18:19:49 +00:00
|
|
|
spec.nested_sep(), spec.nested_sep_len(), args...);
|
2015-07-04 01:18:21 +00:00
|
|
|
if (sw < 0) return sw;
|
|
|
|
written += sw;
|
|
|
|
continue;
|
|
|
|
}
|
2015-07-04 00:18:14 +00:00
|
|
|
if (!argpos) {
|
2015-07-03 20:46:06 +00:00
|
|
|
argpos = argidx++;
|
2015-07-04 18:19:49 +00:00
|
|
|
if (spec.arg_width()) {
|
|
|
|
if (!spec.set_width(argpos - 1, args...))
|
2015-07-04 00:18:14 +00:00
|
|
|
return -1;
|
|
|
|
argpos = argidx++;
|
2015-07-03 20:46:06 +00:00
|
|
|
}
|
2015-07-04 18:19:49 +00:00
|
|
|
if (spec.arg_precision()) {
|
|
|
|
if (!spec.set_precision(argpos - 1, args...))
|
2015-07-04 00:18:14 +00:00
|
|
|
return -1;
|
|
|
|
argpos = argidx++;
|
|
|
|
}
|
|
|
|
} else {
|
2015-07-04 18:19:49 +00:00
|
|
|
bool argprec = spec.arg_precision();
|
2015-07-04 00:18:14 +00:00
|
|
|
if (argprec) {
|
|
|
|
if (argpos <= 1) {
|
|
|
|
assert(false && "argument precision not given");
|
|
|
|
return -1;
|
|
|
|
}
|
2015-07-04 18:19:49 +00:00
|
|
|
if (!spec.set_precision(argpos - 2, args...))
|
2015-07-04 00:18:14 +00:00
|
|
|
return -1;
|
|
|
|
}
|
2015-07-04 18:19:49 +00:00
|
|
|
if (spec.arg_width()) {
|
2015-07-04 00:18:14 +00:00
|
|
|
if (argpos <= (argprec + 1)) {
|
|
|
|
assert(false && "argument width not given");
|
|
|
|
return -1;
|
|
|
|
}
|
2015-07-04 18:19:49 +00:00
|
|
|
if (!spec.set_width(argpos - 2 - argprec, args...))
|
2015-07-04 00:18:14 +00:00
|
|
|
return -1;
|
2015-07-03 20:46:06 +00:00
|
|
|
}
|
|
|
|
}
|
2015-07-05 22:59:36 +00:00
|
|
|
Ptrdiff sw = spec.write_arg(writer, argpos - 1, args...);
|
2015-07-04 00:18:14 +00:00
|
|
|
if (sw < 0) return sw;
|
|
|
|
written += sw;
|
2015-07-03 20:46:06 +00:00
|
|
|
}
|
2015-07-04 00:18:14 +00:00
|
|
|
written += twr;
|
|
|
|
fmtn = retn;
|
|
|
|
return written;
|
2015-07-01 01:22:42 +00:00
|
|
|
}
|
2015-07-04 00:18:14 +00:00
|
|
|
|
|
|
|
template<typename R, typename ...A>
|
2015-07-11 15:07:52 +00:00
|
|
|
inline Ptrdiff format_impl(R &writer, Size &fmtn, bool, const char *fmt) {
|
2015-07-05 22:59:36 +00:00
|
|
|
Size written = 0;
|
|
|
|
detail::WriteSpec spec(fmt, false);
|
2015-07-04 00:18:14 +00:00
|
|
|
if (spec.read_until_spec(writer, &written)) return -1;
|
|
|
|
fmtn = 0;
|
|
|
|
return written;
|
|
|
|
}
|
|
|
|
} /* namespace detail */
|
2015-07-01 17:51:39 +00:00
|
|
|
|
2015-07-03 17:21:05 +00:00
|
|
|
template<typename R, typename ...A>
|
2015-07-11 15:07:52 +00:00
|
|
|
inline Ptrdiff format(R &&writer, Size &fmtn, const char *fmt,
|
|
|
|
const A &...args) {
|
2015-07-05 22:59:36 +00:00
|
|
|
return detail::format_impl(writer, fmtn, false, fmt, args...);
|
2015-07-03 17:21:05 +00:00
|
|
|
}
|
|
|
|
|
2015-07-01 19:09:02 +00:00
|
|
|
template<typename R, typename AL, typename ...A>
|
2015-07-05 22:59:36 +00:00
|
|
|
Ptrdiff format(R &&writer, Size &fmtn, const AnyString<AL> &fmt,
|
|
|
|
const A &...args) {
|
2015-07-03 21:43:54 +00:00
|
|
|
return format(writer, fmtn, fmt.data(), args...);
|
2015-07-01 17:51:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
template<typename R, typename ...A>
|
2015-07-05 22:59:36 +00:00
|
|
|
Ptrdiff format(R &&writer, const char *fmt, const A &...args) {
|
|
|
|
Size fmtn = 0;
|
2015-07-03 21:43:54 +00:00
|
|
|
return format(writer, fmtn, fmt, args...);
|
2015-07-01 01:22:42 +00:00
|
|
|
}
|
|
|
|
|
2015-07-01 19:09:02 +00:00
|
|
|
template<typename R, typename AL, typename ...A>
|
2015-07-05 22:59:36 +00:00
|
|
|
Ptrdiff format(R &&writer, const AnyString<AL> &fmt, const A &...args) {
|
|
|
|
Size fmtn = 0;
|
2015-07-03 21:43:54 +00:00
|
|
|
return format(writer, fmtn, fmt.data(), args...);
|
2015-07-01 01:22:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
} /* namespace octa */
|
|
|
|
|
|
|
|
#endif
|