OSSIA
Open Scenario System for Interactive Application
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 
13 namespace ossia::net
14 {
15 
16 template <typename Socket>
17 struct 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 
24  explicit line_framing_decoder(Socket& socket)
25  : socket{socket}
26  {
27  m_data.reserve(65535);
28  }
29 
30  template <typename F>
31  void receive(F f)
32  {
33  m_data.clear();
34 
35  // Receive the size prefix
36  boost::asio::async_read_until(
37  socket, boost::asio::dynamic_buffer(m_data), (const char*)delimiter,
38  [this, f = std::move(f)](boost::system::error_code ec, std::size_t sz) mutable {
39  if(ec.failed())
40  return;
41 
42  int new_sz = sz;
43  new_sz -= strlen(delimiter);
44  if(new_sz > 0)
45  read_data(std::move(f), ec, new_sz);
46  else
47  this->receive(std::move(f));
48  });
49  }
50 
51  template <typename F>
52  void read_data(F&& f, boost::system::error_code ec, std::size_t sz)
53  {
54  if(!f.validate_stream(ec))
55  return;
56 
57  if(!ec && sz > 0)
58  {
59  try
60  {
61  f((const unsigned char*)m_data.data(), sz);
62  }
63  catch(...)
64  {
65  }
66  }
67 
68  this->receive(std::move(f));
69  }
70 };
71 
72 template <typename Socket>
73 struct line_framing_encoder
74 {
75  Socket& socket;
76  char delimiter[8] = {0};
77 
78  void write(const char* data, std::size_t sz)
79  {
80  this->write(socket, boost::asio::buffer(data, sz));
81  this->write(socket, boost::asio::buffer(delimiter, strlen(delimiter)));
82  }
83 
84  template <typename T>
85  void write(T& sock, const boost::asio::const_buffer& buf)
86  {
87  boost::asio::write(sock, buf);
88  }
89 
90  template <typename T>
91  void write(multi_socket_writer<T>& sock, const boost::asio::const_buffer& buf)
92  {
93  sock.write(buf);
94  }
95 };
96 
97 struct line_framing
98 {
99  template <typename Socket>
100  using encoder = line_framing_encoder<Socket>;
101  template <typename Socket>
102  using decoder = line_framing_decoder<Socket>;
103 };
104 
105 }