Skip to content

Commit 9190069

Browse files
authored
🐛 Don't write beyond passed capacity (#7)
- Add `pen_t::capacity` and track on writing every character. - Don't close styles that don't have scope (reset/clear).
1 parent c8f5c4e commit 9190069

File tree

1 file changed

+19
-7
lines changed

1 file changed

+19
-7
lines changed

include/forest/forest.hpp

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ constexpr std::size_t length(std::string_view const text);
3939
///
4040
template <std::size_t Capacity>
4141
struct literal {
42-
char buffer[Capacity]{};
42+
char buffer[Capacity + 1]{};
4343

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

@@ -226,6 +226,7 @@ struct attribute_t {
226226
template <typename Out>
227227
struct pen_t {
228228
Out out;
229+
std::size_t capacity{std::string_view::npos};
229230

230231
std::size_t written{};
231232

@@ -244,14 +245,26 @@ struct pen_t {
244245
return 1;
245246
}
246247

248+
static constexpr bool has_close(style_t const style) {
249+
switch (style) {
250+
case style_t::reset:
251+
case style_t::clear: return false;
252+
default: return true;
253+
}
254+
}
255+
256+
constexpr bool full() const { return written + 1 >= capacity; }
257+
247258
constexpr pen_t& write_digit(std::uint32_t const digit) {
259+
if (full()) { return *this; }
248260
auto const ch = static_cast<char>(digit + '0');
249261
++written;
250262
return write(std::string_view(&ch, 1));
251263
}
252264

253265
constexpr pen_t& write(std::string_view const text) {
254266
for (char const ch : text) {
267+
if (full()) { return *this; }
255268
*out++ = ch;
256269
++written;
257270
}
@@ -283,7 +296,7 @@ struct pen_t {
283296
constexpr pen_t& write(attr_op_t<style_t> style) {
284297
if (style.t == style_t::clear) { write({style_t::reset}); }
285298
write(attribute_start_v);
286-
if (style.op == op_type::close) {
299+
if (style.op == op_type::close && has_close(style.t)) {
287300
if (style.t == style_t::bold) { style.t = style_t::dim; }
288301
write("2");
289302
}
@@ -300,8 +313,7 @@ struct null_writer {
300313
};
301314

302315
template <typename Out>
303-
constexpr pen_t<Out>& write(pen_t<Out>& out_pen, std::string_view const text, std::size_t capacity = std::string_view::npos) {
304-
auto vacant = [capacity, &out_pen] { return capacity == std::string_view::npos || out_pen.written < capacity; };
316+
constexpr pen_t<Out>& write(pen_t<Out>& out_pen, std::string_view const text) {
305317
auto write_token = [&out_pen](std::string_view const token) -> pen_t<Out>& {
306318
if (token.front() == '<' && token.back() == '>') {
307319
auto attr = attribute_t{};
@@ -316,16 +328,16 @@ constexpr pen_t<Out>& write(pen_t<Out>& out_pen, std::string_view const text, st
316328
};
317329
auto tokenizer = tokenizer_t{text};
318330
auto token = std::string_view{};
319-
while (vacant() && tokenizer(token)) { write_token(token); }
331+
while (!out_pen.full() && tokenizer(token)) { write_token(token); }
320332
return out_pen;
321333
}
322334
} // namespace detail
323335
} // namespace forest
324336

325337
template <typename Out>
326338
constexpr Out forest::format_to(Out out, std::string_view const text, std::size_t capacity) {
327-
auto pen = detail::pen_t<Out>{out};
328-
return detail::write(pen, text, capacity).out;
339+
auto pen = detail::pen_t<Out>{out, capacity};
340+
return detail::write(pen, text).out;
329341
}
330342

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

0 commit comments

Comments
 (0)