Skip to content

🐛 Don't write beyond passed capacity #7

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 15, 2022
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 19 additions & 7 deletions include/forest/forest.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ constexpr std::size_t length(std::string_view const text);
///
template <std::size_t Capacity>
struct literal {
char buffer[Capacity]{};
char buffer[Capacity + 1]{};

constexpr literal(std::string_view const text) { format_to(buffer, text, Capacity); }

Expand Down Expand Up @@ -226,6 +226,7 @@ struct attribute_t {
template <typename Out>
struct pen_t {
Out out;
std::size_t capacity{std::string_view::npos};

std::size_t written{};

Expand All @@ -244,14 +245,26 @@ struct pen_t {
return 1;
}

static constexpr bool has_close(style_t const style) {
switch (style) {
case style_t::reset:
case style_t::clear: return false;
default: return true;
}
}

constexpr bool full() const { return written + 1 >= capacity; }

constexpr pen_t& write_digit(std::uint32_t const digit) {
if (full()) { return *this; }
auto const ch = static_cast<char>(digit + '0');
++written;
return write(std::string_view(&ch, 1));
}

constexpr pen_t& write(std::string_view const text) {
for (char const ch : text) {
if (full()) { return *this; }
*out++ = ch;
++written;
}
Expand Down Expand Up @@ -283,7 +296,7 @@ struct pen_t {
constexpr pen_t& write(attr_op_t<style_t> style) {
if (style.t == style_t::clear) { write({style_t::reset}); }
write(attribute_start_v);
if (style.op == op_type::close) {
if (style.op == op_type::close && has_close(style.t)) {
if (style.t == style_t::bold) { style.t = style_t::dim; }
write("2");
}
Expand All @@ -300,8 +313,7 @@ struct null_writer {
};

template <typename Out>
constexpr pen_t<Out>& write(pen_t<Out>& out_pen, std::string_view const text, std::size_t capacity = std::string_view::npos) {
auto vacant = [capacity, &out_pen] { return capacity == std::string_view::npos || out_pen.written < capacity; };
constexpr pen_t<Out>& write(pen_t<Out>& out_pen, std::string_view const text) {
auto write_token = [&out_pen](std::string_view const token) -> pen_t<Out>& {
if (token.front() == '<' && token.back() == '>') {
auto attr = attribute_t{};
Expand All @@ -316,16 +328,16 @@ constexpr pen_t<Out>& write(pen_t<Out>& out_pen, std::string_view const text, st
};
auto tokenizer = tokenizer_t{text};
auto token = std::string_view{};
while (vacant() && tokenizer(token)) { write_token(token); }
while (!out_pen.full() && tokenizer(token)) { write_token(token); }
return out_pen;
}
} // namespace detail
} // namespace forest

template <typename Out>
constexpr Out forest::format_to(Out out, std::string_view const text, std::size_t capacity) {
auto pen = detail::pen_t<Out>{out};
return detail::write(pen, text, capacity).out;
auto pen = detail::pen_t<Out>{out, capacity};
return detail::write(pen, text).out;
}

constexpr std::size_t forest::length(std::string_view const text) {
Expand Down