2#include <ossia/detail/pod_vector.hpp>
3#include <ossia/network/sockets/writers.hpp>
5#include <boost/asio/buffer.hpp>
6#include <boost/asio/error.hpp>
7#include <boost/asio/read.hpp>
8#include <boost/asio/read_until.hpp>
9#include <boost/asio/streambuf.hpp>
10#include <boost/asio/write.hpp>
11#include <boost/endian/conversion.hpp>
24template <std::
size_t N>
25inline std::size_t set_line_delimiter(
char (&buffer)[N], std::string_view delim)
noexcept
27 static_assert(N >= 1);
28 const std::size_t sz = std::min(delim.size(), N - 1);
29 std::copy_n(delim.data(), sz, buffer);
30 std::fill(buffer + sz, buffer + N,
'\0');
34template <
typename Socket>
35struct line_framing_decoder
37 using buffer_type = std::vector<char, ossia::pod_allocator_avx2<char>>;
40 char delimiter[8] = {0};
41 int32_t m_next_packet_size{};
58 std::shared_ptr<buffer_type> m_data{std::make_shared<buffer_type>()};
59 uint8_t m_delimiter_len = 0;
60 lifetime_token m_lifetime;
62 explicit line_framing_decoder(Socket& socket)
65 m_data->reserve(65535);
68 void set_delimiter(std::string_view delim)
noexcept
70 m_delimiter_len = uint8_t(set_line_delimiter(delimiter, delim));
76 if(m_delimiter_len == 0)
77 m_delimiter_len = strnlen(delimiter, 8);
81 boost::asio::async_read_until(
82 socket, boost::asio::dynamic_buffer(*m_data), (
const char*)delimiter,
83 [
this, alive = m_lifetime.watch(), buf = m_data,
84 f = std::move(f)](boost::system::error_code ec, std::size_t sz)
mutable {
92 read_data(std::move(f), ec, sz);
98 void read_data(F&& f, boost::system::error_code ec, std::size_t sz)
100 if(!f.validate_stream(ec))
109 dispatch_lines(f, sz);
111 this->receive(std::move(f));
115 template <
typename F>
116 void dispatch_lines(
const F& f, std::size_t sz)
118 auto& data = *m_data;
119 std::size_t consumed = 0;
120 std::size_t end = sz;
124 if(end > consumed + m_delimiter_len)
128 f((
const unsigned char*)data.data() + consumed,
129 end - consumed - m_delimiter_len);
138 const char*
const begin = data.data();
139 const char*
const last = begin + data.size();
140 const char*
const next
141 = std::search(begin + consumed, last, delimiter, delimiter + m_delimiter_len);
144 end = std::size_t(next - begin) + m_delimiter_len;
147 data.erase(data.begin(), data.begin() + consumed);
151template <
typename Socket>
152struct line_framing_encoder
155 char delimiter[8] = {0};
156 uint8_t delimiter_len = 0;
158 void set_delimiter(std::string_view delim)
noexcept
160 delimiter_len = uint8_t(set_line_delimiter(delimiter, delim));
163 void write(
const char* data, std::size_t sz)
165 if(delimiter_len == 0)
166 delimiter_len = strnlen(delimiter, 8);
169 std::array<boost::asio::const_buffer, 2> bufs = {
170 boost::asio::buffer(data, sz),
171 boost::asio::buffer(delimiter, delimiter_len)};
172 this->do_write(socket, bufs);
176 template <
typename T, std::
size_t N>
177 void do_write(T& sock,
const std::array<boost::asio::const_buffer, N>& bufs)
179 boost::asio::write(sock, bufs);
183 template <
typename T, std::
size_t N>
185 multi_socket_writer<T>& sock,
186 const std::array<boost::asio::const_buffer, N>& bufs)
188 for(
const auto& buf : bufs)
195 template <
typename Socket>
196 using encoder = line_framing_encoder<Socket>;
197 template <
typename Socket>
198 using decoder = line_framing_decoder<Socket>;