0
0
Fork 0
mirror of https://github.com/matrix-construct/construct synced 2024-12-26 07:23:53 +01:00

ircd::fmt: Modernize buffering, cleanup, comment various.

This commit is contained in:
Jason Volk 2018-11-28 13:34:49 -08:00
parent ad5305f151
commit 24ad230aed
2 changed files with 94 additions and 89 deletions

View file

@ -26,7 +26,8 @@ namespace ircd::fmt
struct snstringf;
struct vsnstringf;
template<size_t MAX> struct bsprintf;
using arg = std::tuple<const void *, std::type_index>;
using arg = std::tuple<const void *, const std::type_index &>;
}
/// Typesafe snprintf() from formal grammar and RTTI.
@ -41,38 +42,34 @@ namespace ircd::fmt
///
class ircd::fmt::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
window_buffer out; // Window on the output buffer.
const_buffer fmt; // Current running position in the fmtstr.
short idx; // Keeps count of the args for better err msgs
protected:
bool finished() const;
size_t remaining() const;
size_t consumed() const { return out - obeg; }
auto &buffer() const { return obeg; }
size_t consumed() const { return out.consumed(); }
string_view completed() const { return out.completed(); }
void append(const char *const &begin, const char *const &end);
void append(const const_buffer &);
void argument(const arg &);
IRCD_OVERLOAD(internal)
snprintf(internal_t, char *const &, const size_t &, const char *const &, const va_rtti &);
snprintf(internal_t, const mutable_buffer &, const string_view &, const va_rtti &);
public:
operator ssize_t() const { return consumed(); }
operator string_view() const { return { obeg, consumed() }; }
operator string_view() const { return completed(); }
template<class... Args>
snprintf(char *const &buf,
const size_t &max,
const char *const &fmt,
const string_view &fmt,
Args&&... args)
:snprintf
{
internal, buf, max, fmt, va_rtti{std::forward<Args>(args)...}
internal, mutable_buffer{buf, max}, fmt, va_rtti{std::forward<Args>(args)...}
}{}
};
@ -81,11 +78,11 @@ struct ircd::fmt::sprintf
{
template<class... Args>
sprintf(const mutable_buffer &buf,
const char *const &fmt,
const string_view &fmt,
Args&&... args)
:snprintf
{
internal, data(buf), size(buf), fmt, va_rtti{std::forward<Args>(args)...}
internal, buf, fmt, va_rtti{std::forward<Args>(args)...}
}{}
};
@ -105,11 +102,11 @@ struct ircd::fmt::vsnprintf
{
vsnprintf(char *const &buf,
const size_t &max,
const char *const &fmt,
const string_view &fmt,
const va_rtti &ap)
:snprintf
{
internal, buf, max, fmt, ap
internal, mutable_buffer{buf, max}, fmt, ap
}{}
};
@ -117,11 +114,11 @@ struct ircd::fmt::vsprintf
:snprintf
{
vsprintf(const mutable_buffer &buf,
const char *const &fmt,
const string_view &fmt,
const va_rtti &ap)
:snprintf
{
internal, data(buf), size(buf), fmt, ap
internal, buf, fmt, ap
}{}
};
@ -129,16 +126,17 @@ struct ircd::fmt::vsnstringf
:std::string
{
vsnstringf(const size_t &max,
const char *const &fmt,
const string_view &fmt,
const va_rtti &ap)
:std::string
{
ircd::string(max, [&fmt, &ap]
(const mutable_buffer &buf) -> string_view
{
using buffer::data;
using buffer::size;
return vsnprintf(data(buf), size(buf), fmt, ap);
return vsprintf
{
buf, fmt, ap
};
})
}{}
};
@ -148,7 +146,7 @@ struct ircd::fmt::snstringf
{
template<class... args>
snstringf(const size_t &max,
const char *const &fmt,
const string_view &fmt,
args&&... a)
:vsnstringf
{
@ -164,11 +162,11 @@ struct ircd::fmt::bsprintf
std::array<char, MAX> buf;
template<class... args>
bsprintf(const char *const &fmt,
bsprintf(const string_view &fmt,
args&&... a)
:snprintf
{
internal, buf.data(), buf.size(), fmt, va_rtti{std::forward<args>(a)...}
internal, buf, fmt, va_rtti{std::forward<args>(a)...}
}
,string_view
{

View file

@ -31,12 +31,13 @@ namespace ircd::fmt
extern std::map<string_view, specifier *, std::less<>> specifiers;
bool is_specifier(const string_view &name);
void handle_specifier(char *&out, const size_t &max, const uint &idx, const spec &, const arg &);
void handle_specifier(mutable_buffer &out, const uint &idx, const spec &, const arg &);
template<class generator> bool generate_string(char *&out, const generator &gen, const arg &val);
template<class T, class lambda> bool visit_type(const arg &val, lambda&& closure);
}
// Structural representation of a format specifier
/// Structural representation of a format specifier. The parse of each
/// specifier in the format string creates one of these.
struct ircd::fmt::spec
{
char sign {'+'};
@ -47,17 +48,21 @@ struct ircd::fmt::spec
spec() = default;
};
/// Reflects the fmt::spec struct to allow the spirit::qi grammar to directly
/// fill in the spec struct.
BOOST_FUSION_ADAPT_STRUCT
(
ircd::fmt::spec,
( decltype(ircd::fmt::spec::sign), sign )
( decltype(ircd::fmt::spec::width), width )
( decltype(ircd::fmt::spec::sign), sign )
( decltype(ircd::fmt::spec::width), width )
( decltype(ircd::fmt::spec::precision), precision )
( decltype(ircd::fmt::spec::name), name )
( decltype(ircd::fmt::spec::name), name )
)
// A format specifier handler module.
// This allows a new "%foo" to be defined with custom handling.
/// A format specifier handler module. This allows a new "%foo" to be defined
/// with custom handling by overriding. This abstraction is inserted into a
/// mapping key'ed by the supplied names leading to an instance of this.
///
class ircd::fmt::specifier
{
std::set<std::string> names;
@ -70,9 +75,11 @@ class ircd::fmt::specifier
virtual ~specifier() noexcept;
};
/// Linkage for the lookup mapping of registered format specifiers.
decltype(ircd::fmt::specifiers)
ircd::fmt::specifiers;
/// The format string parser grammar.
struct ircd::fmt::parser
:qi::grammar<const char *, fmt::spec>
{
@ -305,45 +312,45 @@ const pointer_specifier
using namespace ircd;
fmt::snprintf::snprintf(internal_t,
char *const &out,
const size_t &max,
const char *const &fstr,
const mutable_buffer &out,
const string_view &fmt,
const va_rtti &v)
try
:fstart{strchr(fstr, SPECIFIER)}
,fstop{fstr}
,fend{fstr + strnlen(fstr, max)}
,obeg{out}
,oend{out + max}
,out{out}
:out{out}
,fmt{[&fmt]
{
// start the member fmt variable at the first specifier (or end)
const auto pos(fmt.find(SPECIFIER));
return pos != fmt.npos?
fmt.substr(pos):
string_view{};
}()}
,idx{0}
{
if(unlikely(!max))
// If out has no size we have nothing to do, not even null terminate it.
if(unlikely(empty(out)))
return;
// If fmt has no specifiers then we can just copy the fmt as best as
// possible to the out buffer.
if(empty(this->fmt))
{
fstart = nullptr;
consume(this->out, strlcpy(this->out, fmt));
return;
}
if(!fstart)
{
const mutable_buffer dst{out, max};
const const_buffer src{fstr, fend};
this->out += size_t(strlcpy(dst, src));
return;
}
// Copy everything from fmt up to the first specifier.
assert(data(this->fmt) >= data(fmt));
append(const_buffer(data(fmt), data(this->fmt)));
append(fstr, fstart);
// Iterate
auto it(begin(v));
for(size_t i(0); i < v.size() && !finished(); ++it, i++)
{
const auto &ptr(get<0>(*it));
const auto &type(get<1>(*it));
argument(std::make_tuple(ptr, std::type_index(*type)));
const void *const &ptr(get<0>(*it));
const std::type_index type(*get<1>(*it));
argument(std::make_tuple(ptr, type));
}
assert(this->out >= obeg);
assert(this->out < oend);
*this->out = '\0';
}
catch(const std::out_of_range &e)
{
@ -356,50 +363,48 @@ catch(const std::out_of_range &e)
void
fmt::snprintf::argument(const arg &val)
{
// The format string's front pointer is sitting on the specifier '%'
// waiting to be parsed now.
fmt::spec spec;
if(qi::parse(fstart, fend, parser, spec))
handle_specifier(out, remaining(), idx++, spec, val);
auto &start(std::get<0>(this->fmt));
const auto &stop(std::get<1>(this->fmt));
if(qi::parse(start, stop, parser, spec))
handle_specifier(this->out, idx++, spec, val);
fstop = fstart;
if(fstart >= fend)
return;
fstart = strchr(fstart, SPECIFIER);
append(fstop, fstart?: fend);
// The parse advanced the front pointer of this->fmt to after the
// specifier. Now we copy characters from here up until the
// next specifier.
const string_view fmt(start, stop);
const auto nextpos(fmt.find(SPECIFIER));
append(const_buffer(fmt.substr(0, nextpos)));
this->fmt = const_buffer
{
nextpos != fmt.npos?
fmt.substr(nextpos):
string_view{}
};
}
void
fmt::snprintf::append(const char *const &begin,
const char *const &end)
fmt::snprintf::append(const const_buffer &src)
{
assert(begin <= end);
const size_t rem(remaining());
const size_t len(end - begin);
const size_t &cpsz
{
std::min(len, rem)
};
memcpy(out, begin, cpsz);
out += cpsz;
assert(out < oend);
consume(out, strlcpy(out, src));
}
size_t
fmt::snprintf::remaining()
const
{
assert(out < oend);
assert(obeg <= oend);
const ssize_t r(oend - out);
return std::max(r - 1, ssize_t(0));
return out.remaining()?
out.remaining() - 1:
0;
}
bool
fmt::snprintf::finished()
const
{
return !fstart || fstop >= fend || remaining() == 0;
return empty(fmt) || !remaining();
}
fmt::specifier::specifier(const std::string &name)
@ -435,8 +440,7 @@ fmt::is_specifier(const string_view &name)
}
void
fmt::handle_specifier(char *&out,
const size_t &max,
fmt::handle_specifier(mutable_buffer &out,
const uint &idx,
const spec &spec,
const arg &val)
@ -444,7 +448,10 @@ try
{
const auto &type(get<1>(val));
const auto &handler(*specifiers.at(spec.name));
if(unlikely(!handler(out, max, spec, val)))
auto &outp(std::get<0>(out));
const size_t max(size(out));
if(unlikely(!handler(outp, max, spec, val)))
throw invalid_type
{
"`%s' (%s) for format specifier '%s' for argument #%u",