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:
parent
ccac92704d
commit
5dd280bb3b
2 changed files with 99 additions and 80 deletions
|
@ -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
|
||||
|
|
116
ircd/fmt.cc
116
ircd/fmt.cc
|
@ -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()
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue