OSSIA
Open Scenario System for Interactive Application
Loading...
Searching...
No Matches
line_framing.hpp
1#pragma once
2#include <ossia/detail/pod_vector.hpp>
3#include <ossia/network/sockets/writers.hpp>
4
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>
12
13namespace ossia::net
14{
15
16template <typename Socket>
17struct line_framing_decoder
18{
19 Socket& socket;
20 char delimiter[8] = {0};
21 int32_t m_next_packet_size{};
22 std::vector<char, ossia::pod_allocator_avx2<char>> m_data;
23 uint8_t m_delimiter_len = 0;
24
25 explicit line_framing_decoder(Socket& socket)
26 : socket{socket}
27 {
28 m_data.reserve(65535);
29 }
30
31 template <typename F>
32 void receive(F f)
33 {
34 if(m_delimiter_len == 0)
35 m_delimiter_len = strnlen(delimiter, 8);
36 m_data.clear();
37
38 // Receive until delimiter
39 boost::asio::async_read_until(
40 socket, boost::asio::dynamic_buffer(m_data), (const char*)delimiter,
41 [this, f = std::move(f)](boost::system::error_code ec, std::size_t sz) mutable {
42 if(ec.failed())
43 return;
44
45 int new_sz = sz;
46 new_sz -= m_delimiter_len;
47 if(new_sz > 0)
48 read_data(std::move(f), ec, new_sz);
49 else
50 this->receive(std::move(f));
51 });
52 }
53
54 template <typename F>
55 void read_data(F&& f, boost::system::error_code ec, std::size_t sz)
56 {
57 if(!f.validate_stream(ec))
58 return;
59
60 if(!ec && sz > 0)
61 {
62 try
63 {
64 f((const unsigned char*)m_data.data(), sz);
65 }
66 catch(...)
67 {
68 }
69 }
70
71 this->receive(std::move(f));
72 }
73};
74
75template <typename Socket>
76struct line_framing_encoder
77{
78 Socket& socket;
79 char delimiter[8] = {0};
80 uint8_t delimiter_len = 0;
81
82 void write(const char* data, std::size_t sz)
83 {
84 if(delimiter_len == 0)
85 delimiter_len = strnlen(delimiter, 8);
86
87 // Scatter-gather: data + delimiter in single write
88 std::array<boost::asio::const_buffer, 2> bufs = {
89 boost::asio::buffer(data, sz),
90 boost::asio::buffer(delimiter, delimiter_len)};
91 this->do_write(socket, bufs);
92 }
93
94 // Regular socket: scatter-gather (single syscall)
95 template <typename T, std::size_t N>
96 void do_write(T& sock, const std::array<boost::asio::const_buffer, N>& bufs)
97 {
98 boost::asio::write(sock, bufs);
99 }
100
101 // Multi socket: write each buffer to each socket
102 template <typename T, std::size_t N>
103 void do_write(
104 multi_socket_writer<T>& sock,
105 const std::array<boost::asio::const_buffer, N>& bufs)
106 {
107 for(const auto& buf : bufs)
108 sock.write(buf);
109 }
110};
111
112struct line_framing
113{
114 template <typename Socket>
115 using encoder = line_framing_encoder<Socket>;
116 template <typename Socket>
117 using decoder = line_framing_decoder<Socket>;
118};
119
120}