0
0
Fork 0
mirror of https://github.com/matrix-construct/construct synced 2024-09-28 11:48:54 +02:00

ircd::spirit: Add comments documenting generator_state; assertions; minor cleanup.

This commit is contained in:
Jason Volk 2020-09-03 16:47:33 -07:00
parent 91485016e2
commit 43afc1a9a7

View file

@ -27,8 +27,15 @@ __attribute__((visibility("default")))
IRCD_EXCEPTION(error, generator_error); IRCD_EXCEPTION(error, generator_error);
IRCD_EXCEPTION(generator_error, buffer_overrun); IRCD_EXCEPTION(generator_error, buffer_overrun);
IRCD_SPIRIT_GSPTR_LINKAGE thread_local struct generator_state *generator_state; constexpr size_t
extern thread_local char generator_buffer[8][64_KiB]; generator_buffer_size {64_KiB},
generator_buffer_count {8};
IRCD_SPIRIT_GSPTR_LINKAGE thread_local struct generator_state *
generator_state;
extern thread_local char
generator_buffer[generator_buffer_count][generator_buffer_size];
}} }}
namespace ircd { namespace ircd {
@ -44,7 +51,10 @@ __attribute__((visibility("internal")))
| karma::generator_properties::disabling | karma::generator_properties::disabling
>; >;
using sink_type = karma::detail::output_iterator<char *, prop_mask, unused_type>; using sink_type = karma::detail::output_iterator
<
char *, prop_mask, unused_type
>;
template<bool truncation = false, template<bool truncation = false,
class gen, class gen,
@ -52,23 +62,46 @@ __attribute__((visibility("internal")))
bool generate(mutable_buffer &out, gen&&, attr&&...); bool generate(mutable_buffer &out, gen&&, attr&&...);
}} }}
/// This structure is a shadow for the default spirit::karma buffering
/// stack. They conduct buffering by allocating and copying standard strings
/// to implement certain karma directives like "right_align[]" etc. Instead
/// we reimplement optimized buffering with assumptions specific to our
/// application's tolerances. We assume a maximum size of a buffer and maximum
/// height of any stack growing from an ircd::spirit::generate() call without
/// need for reentrancy. This gives us the ability to pre-allocate thread_local
/// buffers.
struct [[gnu::visibility("hidden")]] struct [[gnu::visibility("hidden")]]
ircd::spirit::generator_state ircd::spirit::generator_state
{ {
/// The number of instances stacked behind the current state /// The number of instances stacked behind the current state. This should
/// never be greater than the generator_buffer_count
static uint depth() noexcept; static uint depth() noexcept;
/// User's buffer. /// Destination buffer for this level of the stack. For depth=0 this will
/// be the user's buffer, otherwise it will be the thread-local generator
/// buffer at depth - 1.
///
/// N.B. The `begin()` of this buffer will be advanced by spirit when the
/// depth=0 as part of the output iterator process. It is never touched when
/// depth>0 because it directly refers to one of the static buffers.
mutable_buffer &out; mutable_buffer &out;
/// Previous in buffer stack /// Previous in buffer stack. This is null for the instance created in the
/// frame for ircd::spirit::generate(), which is the base of the stack.
struct generator_state *prev {nullptr}; struct generator_state *prev {nullptr};
/// The number of characters we're storing in buffer /// The number of characters we're storing in buffer at `out`. When
/// `depth>0` this will be the number of characters stored in one of the
/// static buffers between `begin(out), begin(out)+consumed` and safely
/// saturates at the maximum size of the buffer. When `depth=0` this
/// indicates the number of characters *behind* `begin(out)` it still
/// safely saturates at the maximum size of the user's buffer.
uint consumed {0}; uint consumed {0};
/// The number of characters attempted, which may be larger than /// The number of characters attempted, which may be larger than the buffer
/// the buffer capacity indicating an overflow amount. /// capacity and value stored in `consumed`. Thus the difference between
/// generated and consumed is an overflow value which estimates the number
/// of characters which would've been required for the generation.
uint generated {0}; uint generated {0};
}; };
@ -82,13 +115,22 @@ ircd::spirit::generate(mutable_buffer &out,
attr&&... a) attr&&... a)
{ {
const auto max(size(out)); // Save the user buffer as originally provided in case we need to restore it.
const auto start(data(out)); const mutable_buffer user
{
out
};
// Base instance for the buffering stack (see `struct generator_state`).
struct generator_state state struct generator_state state
{ {
out out
}; };
// Set the thread-local generator_state pointer to our stack instance. This
// will cooperatively restore whatever value was previously there, though
// it should be null.
assert(!generator_state);
const scope_restore _state const scope_restore _state
{ {
generator_state, &state generator_state, &state
@ -99,17 +141,21 @@ ircd::spirit::generate(mutable_buffer &out,
begin(out) begin(out)
}; };
// Enter into `spirit::karma` to execute the generator
const auto ret const auto ret
{ {
karma::generate(sink, std::forward<gen>(g), std::forward<attr>(a)...) karma::generate(sink, std::forward<gen>(g), std::forward<attr>(a)...)
}; };
// Determine if the user's buffer was enough to store the result; if not
// there will be an overflow value.
assert(state.generated >= state.consumed); assert(state.generated >= state.consumed);
const auto overflow const auto overflow
{ {
state.generated - state.consumed state.generated - state.consumed
}; };
// Compile-time branch for generators that tolerate truncation (i.e. fmt::)
if constexpr(truncation) if constexpr(truncation)
{ {
begin(out) = begin(out) > end(out)? end(out): begin(out); begin(out) = begin(out) > end(out)? end(out): begin(out);
@ -117,9 +163,11 @@ ircd::spirit::generate(mutable_buffer &out,
return ret; return ret;
} }
// Branch to throw an exception for generators that do not tolerate
// truncated results (i.e. json::).
if(unlikely(overflow || begin(out) > end(out))) if(unlikely(overflow || begin(out) > end(out)))
{ {
begin(out) = start; begin(out) = begin(user);
assert(begin(out) <= end(out)); assert(begin(out) <= end(out));
char pbuf[2][48]; char pbuf[2][48];
@ -131,7 +179,7 @@ ircd::spirit::generate(mutable_buffer &out,
}; };
} }
assert(begin(out) >= end(out) - max); assert(begin(out) >= end(out) - size(user));
assert(begin(out) <= end(out)); assert(begin(out) <= end(out));
return ret; return ret;
} }