0
0
Fork 0
mirror of https://github.com/matrix-construct/construct synced 2024-12-25 23:14:13 +01:00

ircd::fmt: snprintf -> category.

This commit is contained in:
Jason Volk 2016-09-18 01:15:40 -07:00
parent ccac92704d
commit 5dd280bb3b
2 changed files with 99 additions and 80 deletions

View file

@ -32,7 +32,7 @@ IRCD_EXCEPTION(error, invalid_type);
IRCD_EXCEPTION(error, illegal);
//
// module/internal API
// Module API
//
extern const char SPECIFIER;
extern const char SPECIFIER_TERMINATOR;
@ -66,37 +66,52 @@ class specifier
};
const std::map<std::string, specifier *> &specifiers();
ssize_t _snprintf(char *const &, const size_t &, const char *const &, const ptrs &, const types &);
//
// public API
// User API
//
// Implementation of the traditional snprintf(), as best as practical:
// * The arguments are not restricted by va_list limitations. You can pass a real std::string.
// * The function participates in the custom protocol-safe ruleset, and the behavior is non-standard.
// To be sure to get truly /standard/ snprintf() behavior use ::snprintf() instead.
template<class... Args> ssize_t snprintf(char *const &buf, const size_t &max, const char *const &fmt, Args&&... args);
// * The arguments are not restricted by stdarg limitations. You can pass a real std::string.
// * The function participates in the custom protocol-safe ruleset.
class snprintf
{
const char *fstart; // Current running position in the fmtstr
const char *fstop; // Saved state from the last position
const char *fend; // past-the-end iterator of the fmtstr
const char *obeg; // Saved beginning of the output buffer
const char *oend; // past-the-end iterator of the output buffer
char *out; // Current running position of the output buffer
short idx; // Keeps count of the args for better err msgs
protected:
auto finished() const { return !fstart || fstop >= fend; }
auto remaining() const { return (oend - out) - 1; }
auto consumed() const { return size_t(out - obeg); }
auto &buffer() const { return obeg; }
void append(const char *const &begin, const char *const &end);
void argument(const arg &);
IRCD_OVERLOAD(internal)
snprintf(internal_t, char *const &, const size_t &, const char *const &, const ptrs &, const types &);
public:
operator ssize_t() const { return consumed(); }
template<class... A>
snprintf(char *const &buf, const size_t &max, const char *const &fmt, A&&... args);
};
template<class... Args>
ssize_t
snprintf(char *const &buf,
const size_t &max,
const char *const &fmt,
Args&&... args)
snprintf::snprintf(char *const &buf,
const size_t &max,
const char *const &fmt,
Args&&... args)
:snprintf
{
internal, buf, max, fmt, ptrs{std::addressof(args)...}, types{typeid(Args)...}
}
{
static const std::vector<std::type_index> types
{
typeid(Args)...
};
const std::vector<const void *> ptrs
{
std::addressof(args)...
};
return _snprintf(buf, max, fmt, ptrs, types);
}
} // namespace fmt

View file

@ -210,75 +210,79 @@ fmt::parse::grammar<it, top>::grammar(qi::rule<it, top> &top_rule)
})];
}
ssize_t
fmt::_snprintf(char *const &buf,
const size_t &max,
const char *const &fmt,
const ptrs &p,
const types &t)
fmt::snprintf::snprintf(internal_t,
char *const &out,
const size_t &max,
const char *const &fstr,
const ptrs &p,
const types &t)
try
:fstart{strchr(fstr, SPECIFIER)}
,fstop{fstr}
,fend{fstr + strlen(fstr)}
,obeg{out}
,oend{out + max}
,out{out}
,idx{0}
{
if(unlikely(!max))
return 0;
char *out(buf); // Always points at next place to write
const char *stop(fmt); // Saves the 'last' place to copy a literal from
const char *start(strchr(fmt, SPECIFIER)); // The running position of the format string parse
const char *const end(fmt + strlen(fmt)); // The end of the format string
// Calculates remaining room in output buffer
const auto remaining([&max, &buf, &out]() -> size_t
{
return max - std::distance(buf, out) - 1;
});
// Copies string data between format specifiers.
const auto copy_literal([&stop, &out, &remaining]
(const char *const &end)
{
const size_t len(std::distance(stop, end));
const size_t &cpsz(std::min(len, remaining()));
memcpy(out, stop, cpsz);
out += cpsz;
});
size_t index(0); // The current position for vectors p and t (specifier count)
for(; start; stop = start++, start = start < end? strchr(start, SPECIFIER) : nullptr)
{
// Copy literal data from where the last parse stopped up to the found specifier
copy_literal(start);
// Instantiate the format specifier grammar
struct parser
:parse::grammar<const char *, fmt::spec>
{
parser(): grammar{grammar::spec} {}
}
static const parser;
// Parse the specifier with the grammar
fmt::spec spec;
if(!qi::parse(start, end, parser, spec))
continue;
// Throws if the format string has more specifiers than arguments.
const arg val{p.at(index), t.at(index)};
handle_specifier(out, remaining(), index, spec, val);
index++;
fstart = nullptr;
return;
}
// If the end of the string is not a format specifier itself, it needs to be copied
if(!start)
copy_literal(end);
if(!fstart)
{
append(fstr, fend);
return;
}
*out = '\0';
return std::distance(buf, out);
append(fstr, fstart);
for(size_t i(0); i < p.size(); ++i)
argument(arg{p.at(i), t.at(i)});
}
catch(const std::out_of_range &e)
{
throw invalid_format("Format string requires more than %zu arguments.", p.size());
}
void
fmt::snprintf::argument(const arg &val)
{
struct parser
:parse::grammar<const char *, fmt::spec>
{
parser(): grammar{grammar::spec} {}
}
static const parser;
if(finished())
return;
fmt::spec spec;
if(qi::parse(fstart, fend, parser, spec))
handle_specifier(out, remaining(), idx++, spec, val);
fstop = fstart++;
if(fstop < fend)
{
fstart = strchr(fstart, SPECIFIER);
append(fstop, fstart?: fend);
}
else *out = '\0';
}
void
fmt::snprintf::append(const char *const &begin,
const char *const &end)
{
const size_t len(std::distance(begin, end));
const size_t &cpsz(std::min(len, size_t(remaining())));
memcpy(out, begin, cpsz);
out += cpsz;
*out = '\0';
}
const decltype(fmt::_specifiers) &
fmt::specifiers()
{