OSSIA
Open Scenario System for Interactive Application
size_prefix_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/streambuf.hpp>
9 #include <boost/asio/write.hpp>
10 #include <boost/endian/conversion.hpp>
11 
12 namespace ossia::net
13 {
14 
15 template <typename Socket>
16 struct size_prefix_decoder
17 {
18  Socket& socket;
19  int32_t m_next_packet_size{};
20  ossia::pod_vector<char> m_data;
21 
22  explicit size_prefix_decoder(Socket& socket)
23  : socket{socket}
24  {
25  m_data.reserve(65535);
26  }
27 
28  template <typename F>
29  void receive(F f)
30  {
31  // Receive the size prefix
32  socket.async_read_some(
33  boost::asio::mutable_buffer(&m_next_packet_size, sizeof(int32_t)),
34  [this, f = std::move(f)](boost::system::error_code ec, std::size_t sz) mutable {
35  read_size(std::move(f), ec, sz);
36  });
37  }
38 
39  template <typename F>
40  void read_size(F&& f, boost::system::error_code ec, std::size_t sz)
41  {
42  if(!f.validate_stream(ec))
43  return;
44 
45  boost::endian::big_to_native_inplace(m_next_packet_size);
46  if(m_next_packet_size <= 0)
47  return;
48 
49  m_data.resize(m_next_packet_size);
50  boost::asio::async_read(
51  socket, boost::asio::mutable_buffer(m_data.data(), m_next_packet_size),
52  boost::asio::transfer_exactly(m_next_packet_size),
53  [this, f = std::move(f)](boost::system::error_code ec, std::size_t sz) mutable {
54  read_data(std::move(f), ec, sz);
55  });
56  }
57 
58  template <typename F>
59  void read_data(F&& f, boost::system::error_code ec, std::size_t sz)
60  {
61  if(!f.validate_stream(ec))
62  return;
63 
64  if(!ec && sz > 0)
65  {
66  try
67  {
68  f((const unsigned char*)m_data.data(), sz);
69  }
70  catch(...)
71  {
72  }
73  }
74 
75  this->receive(std::move(f));
76  }
77 };
78 
79 template <typename Socket>
80 struct size_prefix_encoder
81 {
82  Socket& socket;
83 
84  void write(const char* data, std::size_t sz)
85  {
86  int32_t packet_size = sz;
87  boost::endian::native_to_big_inplace(packet_size);
88  this->write(
89  socket, boost::asio::buffer(
90  reinterpret_cast<const char*>(&packet_size), sizeof(int32_t)));
91  this->write(socket, boost::asio::buffer(data, sz));
92  }
93 
94  template <typename T>
95  void write(T& sock, const boost::asio::const_buffer& buf)
96  {
97  boost::asio::write(sock, buf);
98  }
99 
100  template <typename T>
101  void write(multi_socket_writer<T>& sock, const boost::asio::const_buffer& buf)
102  {
103  sock.write(buf);
104  }
105 };
106 
107 struct size_prefix_framing
108 {
109  template <typename Socket>
110  using encoder = size_prefix_encoder<Socket>;
111  template <typename Socket>
112  using decoder = size_prefix_decoder<Socket>;
113 };
114 
115 }