98 lines
2.4 KiB
C++
98 lines
2.4 KiB
C++
#pragma once
|
|
/**
|
|
* @file linebuffer.hpp
|
|
* @author Eric Mertens <emertens@gmail.com>
|
|
* @brief A line buffering class
|
|
* @version 0.1
|
|
* @date 2023-08-22
|
|
*
|
|
* @copyright Copyright (c) 2023
|
|
*
|
|
*/
|
|
|
|
#include <boost/asio/buffer.hpp>
|
|
|
|
#include <algorithm>
|
|
#include <concepts>
|
|
#include <vector>
|
|
|
|
/**
|
|
* @brief Fixed-size buffer with line-oriented dispatch
|
|
*
|
|
*/
|
|
class LineBuffer
|
|
{
|
|
std::vector<char> buffer;
|
|
|
|
// [buffer.begin(), end_) contains buffered data
|
|
// [end_, buffer.end()) is available buffer space
|
|
std::vector<char>::iterator end_;
|
|
|
|
public:
|
|
/**
|
|
* @brief Construct a new Line Buffer object
|
|
*
|
|
* @param n Buffer size
|
|
*/
|
|
LineBuffer(std::size_t n) : buffer(n), end_{buffer.begin()} {}
|
|
|
|
/**
|
|
* @brief Get the available buffer space
|
|
*
|
|
* @return boost::asio::mutable_buffer
|
|
*/
|
|
auto get_buffer() -> boost::asio::mutable_buffer
|
|
{
|
|
return boost::asio::buffer(&*end_, std::distance(end_, buffer.end()));
|
|
}
|
|
|
|
/**
|
|
* @brief Commit new buffer bytes and dispatch line callback
|
|
*
|
|
* The first n bytes of the buffer will be considered to be
|
|
* populated. The line callback function will be called once
|
|
* per completed line. Those lines are removed from the buffer
|
|
* and the is ready for additional calls to get_buffer and
|
|
* add_bytes.
|
|
*
|
|
* @param n Bytes written to the last call of get_buffer
|
|
* @param line_cb Callback function to run on each completed line
|
|
*/
|
|
auto add_bytes(std::size_t n, std::invocable<char *> auto line_cb) -> void
|
|
{
|
|
auto const start = end_;
|
|
std::advance(end_, n);
|
|
|
|
// new data is now located in [start, end_)
|
|
|
|
// cursor marks the beginning of the current line
|
|
auto cursor = buffer.begin();
|
|
|
|
for (auto nl = std::find(start, end_, '\n');
|
|
nl != end_;
|
|
nl = std::find(cursor, end_, '\n'))
|
|
{
|
|
// Null-terminate the line. Support both \n and \r\n
|
|
if (cursor < nl && *std::prev(nl) == '\r')
|
|
{
|
|
*std::prev(nl) = '\0';
|
|
}
|
|
else
|
|
{
|
|
*nl = '\0';
|
|
}
|
|
|
|
line_cb(&*cursor);
|
|
|
|
cursor = std::next(nl);
|
|
}
|
|
|
|
// If any lines were processed, move all processed lines to
|
|
// the front of the buffer
|
|
if (cursor != buffer.begin())
|
|
{
|
|
end_ = std::move(cursor, end_, buffer.begin());
|
|
}
|
|
}
|
|
};
|