OSSIA
Open Scenario System for Interactive Application
Loading...
Searching...
No Matches
var_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/write.hpp>
9#include <boost/endian/conversion.hpp>
10
11#include <array>
12#include <type_traits>
13
14namespace ossia::net
15{
16
17enum class byte_order
18{
19 big_endian,
20 little_endian
21};
22
23template <int HeaderBytes, byte_order Order = byte_order::big_endian>
24struct var_size_prefix_framing
25{
26 static_assert(HeaderBytes == 1 || HeaderBytes == 2 || HeaderBytes == 4);
27
28 using header_type = std::conditional_t<
29 HeaderBytes == 1, uint8_t, std::conditional_t<HeaderBytes == 2, uint16_t, uint32_t>>;
30
31 template <typename Socket>
32 struct decoder
33 {
34 Socket& socket;
35 header_type m_header{};
36 ossia::pod_vector<char> m_data;
37 std::size_t max_frame_size
38 = HeaderBytes == 1 ? std::size_t(255)
39 : HeaderBytes == 2 ? std::size_t(65535)
40 : std::size_t(0x7FFFFFFF);
41
42 explicit decoder(Socket& socket)
43 : socket{socket}
44 {
45 if constexpr(HeaderBytes == 1)
46 m_data.reserve(256);
47 else
48 m_data.reserve(65535);
49 }
50
51 template <typename F>
52 void receive(F f)
53 {
54 boost::asio::async_read(
55 socket, boost::asio::mutable_buffer(&m_header, sizeof(header_type)),
56 boost::asio::transfer_exactly(sizeof(header_type)),
57 [this,
58 f = std::move(f)](boost::system::error_code ec, std::size_t sz) mutable {
59 read_size(std::move(f), ec, sz);
60 });
61 }
62
63 template <typename F>
64 void read_size(F&& f, boost::system::error_code ec, std::size_t sz)
65 {
66 if(!f.validate_stream(ec))
67 return;
68
69 if constexpr(HeaderBytes > 1)
70 {
71 if constexpr(Order == byte_order::big_endian)
72 boost::endian::big_to_native_inplace(m_header);
73 else
74 boost::endian::little_to_native_inplace(m_header);
75 }
76
77 auto packet_size = static_cast<std::size_t>(m_header);
78 if(packet_size == 0 || packet_size > max_frame_size)
79 return;
80
81 m_data.resize(packet_size, boost::container::default_init);
82 boost::asio::async_read(
83 socket, boost::asio::mutable_buffer(m_data.data(), packet_size),
84 boost::asio::transfer_exactly(packet_size),
85 [this,
86 f = std::move(f)](boost::system::error_code ec, std::size_t sz) mutable {
87 read_data(std::move(f), ec, sz);
88 });
89 }
90
91 template <typename F>
92 void read_data(F&& f, boost::system::error_code ec, std::size_t sz)
93 {
94 if(!f.validate_stream(ec))
95 return;
96
97 if(!ec && sz > 0)
98 {
99 try
100 {
101 f((const unsigned char*)m_data.data(), sz);
102 }
103 catch(...)
104 {
105 }
106 }
107
108 this->receive(std::move(f));
109 }
110 };
111
112 template <typename Socket>
113 struct encoder
114 {
115 Socket& socket;
116
117 void write(const char* data, std::size_t sz)
118 {
119 header_type header = static_cast<header_type>(sz);
120 if constexpr(HeaderBytes > 1)
121 {
122 if constexpr(Order == byte_order::big_endian)
123 boost::endian::native_to_big_inplace(header);
124 else
125 boost::endian::native_to_little_inplace(header);
126 }
127
128 std::array<boost::asio::const_buffer, 2> bufs = {
129 boost::asio::buffer(
130 reinterpret_cast<const char*>(&header), sizeof(header_type)),
131 boost::asio::buffer(data, sz)};
132 this->do_write(socket, bufs);
133 }
134
135 template <typename T, std::size_t N>
136 void do_write(T& sock, const std::array<boost::asio::const_buffer, N>& bufs)
137 {
138 boost::asio::write(sock, bufs);
139 }
140
141 template <typename T, std::size_t N>
142 void do_write(
143 multi_socket_writer<T>& sock,
144 const std::array<boost::asio::const_buffer, N>& bufs)
145 {
146 for(const auto& buf : bufs)
147 sock.write(buf);
148 }
149 };
150};
151
152// Common configurations
153using size_prefix_1byte_framing = var_size_prefix_framing<1>;
154using size_prefix_2byte_be_framing = var_size_prefix_framing<2, byte_order::big_endian>;
155using size_prefix_2byte_le_framing = var_size_prefix_framing<2, byte_order::little_endian>;
156using size_prefix_4byte_be_framing = var_size_prefix_framing<4, byte_order::big_endian>;
157using size_prefix_4byte_le_framing = var_size_prefix_framing<4, byte_order::little_endian>;
158
159}