diff --git a/octa/format.hh b/octa/format.hh new file mode 100644 index 0000000..6d44540 --- /dev/null +++ b/octa/format.hh @@ -0,0 +1,87 @@ +/* Format strings for OctaSTD. + * + * This file is part of OctaSTD. See COPYING.md for futher information. + */ + +#ifndef OCTA_FORMAT_HH +#define OCTA_FORMAT_HH + +#include +#include +#include +#include + +#include "octa/algorithm.hh" +#include "octa/string.hh" + +namespace octa { + +template +static inline void write_val(R &writer, const char *val) { + while (*val) writer.put(*val++); +} + +template +static inline void write_val(R &writer, const octa::String &val) { + for (octa::Size i = 0; i < val.size(); ++i) writer.put(val[i]); +} + +template +static inline void write_val(R &writer, char val) { + writer.put(val); +} + +template +static inline void write_val(R &writer, const T &val) { + write_val(writer, octa::to_string(val)); +} + +template +static inline void write_idx(R &writer, octa::Size idx, const T &v) { + if (idx) assert(false && "not enough format args"); + write_val(writer, v); +} + +template +static inline void write_idx(R &writer, octa::Size idx, const T &v, + A &&...args) { + if (idx) { + write_idx(writer, idx - 1, octa::forward(args)...); + return; + } + write_val(writer, v); +} + + +template +static inline octa::Size formatted_write(R writer, const char *fmt, + A &&...args) { + octa::Size argidx = 0, retn = 0; + while (*fmt) { + if (*fmt == '{') { + octa::Size needidx = argidx; + if (*++fmt != '}') { + char idx[8] = { '\0' }; + for (octa::Size i = 0; isdigit(*fmt); ++i) + idx[i] = *fmt++; + assert((*fmt == '}') && "malformed format string"); + needidx = atoi(idx); + } else ++argidx; + ++fmt; + retn = octa::max(needidx, retn); + write_idx(writer, needidx, octa::forward(args)...); + continue; + } + writer.put(*fmt++); + } + return retn + 1; +} + +template +octa::Size formatted_write(R writer, const octa::String &fmt, A &&...args) { + return formatted_write(writer, fmt.data(), octa::forward(args)...); +} + +} /* namespace octa */ + +#endif \ No newline at end of file diff --git a/octa/io.hh b/octa/io.hh index 07c439f..7d93f8a 100644 --- a/octa/io.hh +++ b/octa/io.hh @@ -10,6 +10,7 @@ #include "octa/string.hh" #include "octa/stream.hh" +#include "octa/format.hh" namespace octa { @@ -151,6 +152,52 @@ static inline void writeln(const T &v, const A &...args) { putc('\n', ::stdout); } +namespace detail { + struct FormatOutRange: octa::OutputRange { + FormatOutRange(): needed(0) {} + octa::Size needed; + char buf[512]; + void put(char v) { + if (needed < sizeof(buf)) + buf[needed] = v; + ++needed; + } + }; +} + +template +static inline void writef(const char *fmt, A &&...args) { + octa::detail::FormatOutRange writer1; + octa::formatted_write(writer1, fmt, + octa::forward(args)...); + if (writer1.needed < sizeof(writer1.buf)) { + fwrite(writer1.buf, 1, writer1.needed, ::stdout); + return; + } + octa::String s; + s.reserve(writer1.needed); + s[writer1.needed] = '\0'; + octa::formatted_write(s.iter(), fmt, octa::forward(args)...); + fwrite(s.data(), 1, writer1.needed, ::stdout); +} + +template +static inline void writef(const octa::String &fmt, A &&...args) { + writef(fmt.data(), octa::forward(args)...); +} + +template +static inline void writefln(const char *fmt, A &&...args) { + writef(fmt, octa::forward(args)...); + putc('\n', ::stdout); +} + +template +static inline void writefln(const octa::String &fmt, A &&...args) { + writef(fmt, octa::forward(args)...); + putc('\n', ::stdout); +} + } /* namespace octa */ #endif \ No newline at end of file