bf3cbc72507e04345ad3d2a4f1d6015da25fc2a5
[invirt/third/libt4.git] / rpc / marshall.h
1 #ifndef marshall_h
2 #define marshall_h
3
4 #include "types.h"
5 #include "rpc_protocol.h"
6
7 class marshall {
8     private:
9         string buf_ = string(rpc_protocol::DEFAULT_RPC_SZ, 0);
10         size_t index_ = rpc_protocol::RPC_HEADER_SZ;
11
12     public:
13         template <typename... Args>
14         marshall(const Args & ... args) {
15             (void)pass{(*this << args)...};
16         }
17
18         void write(const void *p, size_t n) {
19             if (index_+n > buf_.size())
20                 buf_.resize(index_+n);
21             std::copy((char *)p, (char *)p+n, &buf_[index_]);
22             index_ += n;
23         }
24
25         // with header
26         inline operator string() const { return buf_.substr(0,index_); }
27         // without header
28         inline string content() const { return buf_.substr(rpc_protocol::RPC_HEADER_SZ,index_-rpc_protocol::RPC_HEADER_SZ); }
29
30         // letting S be a defaulted template parameter forces the compiler to
31         // delay looking up operator<<(marshall &, rpc_sz_t) until we define it
32         // (i.e. we define an operator for marshalling uint32_t)
33         template <class T, class S=rpc_protocol::rpc_sz_t> inline void
34         write_header(const T & h) {
35             VERIFY(sizeof(T)+sizeof(S) <= rpc_protocol::RPC_HEADER_SZ);
36             size_t saved_sz = index_;
37             index_ = 0;
38             *this << (S)(saved_sz - sizeof(S)) << (T)h;
39             index_ = saved_sz;
40         }
41 };
42
43 class unmarshall {
44     private:
45         string buf_;
46         size_t index_ = rpc_protocol::RPC_HEADER_SZ;
47         bool ok_ = false;
48
49     public:
50         template <typename... Args>
51         unmarshall(const string & s, bool has_header, Args && ... args)
52             : buf_(s) {
53             if (!has_header)
54                 buf_.insert(0, rpc_protocol::RPC_HEADER_SZ, 0);
55             ok_ = (buf_.size() >= rpc_protocol::RPC_HEADER_SZ);
56             (void)pass{(*this >> args)...};
57         }
58
59         inline bool ok() const { return ok_; }
60         inline bool okdone() const { return ok_ && index_ == buf_.size(); }
61
62         void read(void * t, size_t n) {
63             if (index_+n > buf_.size())
64                 ok_ = false;
65             if (ok_) {
66                 std::copy(&buf_[index_], &buf_[index_+n], (char *)t);
67                 index_ += n;
68             }
69         }
70
71         template <class T> inline void
72         read_header(T & h) {
73             VERIFY(sizeof(T)+sizeof(rpc_protocol::rpc_sz_t) <= rpc_protocol::RPC_HEADER_SZ);
74             // first 4 bytes hold length field
75             index_ = sizeof(rpc_protocol::rpc_sz_t);
76             *this >> h;
77             index_ = rpc_protocol::RPC_HEADER_SZ;
78         }
79
80         template <class T> inline T _grab() { T t; *this >> t; return t; }
81 };
82
83 //
84 // Marshalling for plain old data
85 //
86
87 #define MARSHALL_RAW_NETWORK_ORDER_AS(_c_, _d_) \
88 inline marshall & operator<<(marshall & m, _c_ x) { _d_ y = hton((_d_)x); m.write(&y, sizeof(_d_)); return m; } \
89 inline unmarshall & operator>>(unmarshall & u, _c_ & x) { _d_ y; u.read(&y, sizeof(_d_)); x = (_c_)ntoh(y); return u; }
90
91 #define MARSHALL_RAW_NETWORK_ORDER(_c_) MARSHALL_RAW_NETWORK_ORDER_AS(_c_, _c_)
92
93 MARSHALL_RAW_NETWORK_ORDER_AS(bool, uint8_t)
94 MARSHALL_RAW_NETWORK_ORDER(uint8_t)
95 MARSHALL_RAW_NETWORK_ORDER(int8_t)
96 MARSHALL_RAW_NETWORK_ORDER(uint16_t)
97 MARSHALL_RAW_NETWORK_ORDER(int16_t)
98 MARSHALL_RAW_NETWORK_ORDER(uint32_t)
99 MARSHALL_RAW_NETWORK_ORDER(int32_t)
100 MARSHALL_RAW_NETWORK_ORDER_AS(size_t, uint32_t)
101 MARSHALL_RAW_NETWORK_ORDER(uint64_t)
102 MARSHALL_RAW_NETWORK_ORDER(int64_t)
103
104 //
105 // Marshalling for tuples (used to implement marshalling for structs)
106 //
107
108 // In order to iterate over the tuple elements, we first need a template
109 // parameter pack containing the tuple's indices.  The function templates named
110 // *_imp below accept an empty tag struct as their last argument, and use its
111 // template arguments to index the tuple.  The operator<< overloads instantiate
112 // the appropriate tag struct to make this possible.
113
114 template <class... Args, size_t... Indices> inline marshall &
115 tuple_marshall_imp(marshall & m, tuple<Args...> & t, std::index_sequence<Indices...>) {
116     // Note that brace initialization is used for the empty structure "pack",
117     // forcing the comma-separated expressions expanded from the parameter pack
118     // to be evaluated in order.  Order matters because the elements must be
119     // serialized consistently!  The empty struct resulting from construction
120     // is discarded.
121     (void)pass{(m << std::get<Indices>(t))...};
122     return m;
123 }
124
125 template <class... Args> inline marshall &
126 operator<<(marshall & m, tuple<Args...> && t) {
127     return tuple_marshall_imp(m, t, std::index_sequence_for<Args...>{});
128 }
129
130 template <class... Args, size_t... Indices> inline unmarshall &
131 tuple_unmarshall_imp(unmarshall & u, tuple<Args & ...> t, std::index_sequence<Indices...>) {
132     (void)pass{(u >> std::get<Indices>(t))...};
133     return u;
134 }
135
136 template <class... Args> inline unmarshall &
137 operator>>(unmarshall & u, tuple<Args & ...> && t) {
138     return tuple_unmarshall_imp(u, t, std::index_sequence_for<Args...>{});
139 }
140
141 //
142 // Marshalling for structs or classes containing a MEMBERS declaration
143 //
144
145 // Implements struct marshalling via tuple marshalling of members.
146 template <class T> inline typename
147 enable_if<is_tuple_convertible<T>::value, unmarshall>::type &
148 operator>>(unmarshall & u, T & a) { return u >> a._tuple_(); }
149
150 template <class T> inline typename
151 enable_if<is_tuple_convertible<T>::value, marshall>::type &
152 operator<<(marshall & m, const T a) { return m << a._tuple_(); }
153
154 //
155 // Marshalling for STL containers
156 //
157
158 // this overload is visible for type A only if A::cbegin and A::cend exist
159 template <class A> inline typename
160 enable_if<is_const_iterable<A>::value, marshall>::type &
161 operator<<(marshall & m, const A & x) {
162     m << (uint32_t)x.size();
163     for (const auto & a : x)
164         m << a;
165     return m;
166 }
167
168 // visible for type A if A::emplace_back(a) makes sense
169 template <class A> inline typename
170 enable_if<supports_emplace_back<A>::value, unmarshall>::type &
171 operator>>(unmarshall & u, A & x) {
172     uint32_t n = u._grab<uint32_t>();
173     x.clear();
174     while (n--)
175         x.emplace_back(u._grab<typename A::value_type>());
176     return u;
177 }
178
179 // std::pair<A, B>
180 template <class A, class B> inline marshall &
181 operator<<(marshall & m, const std::pair<A,B> & d) {
182     return m << d.first << d.second;
183 }
184
185 template <class A, class B> inline unmarshall &
186 operator>>(unmarshall & u, std::pair<A,B> & d) {
187     return u >> d.first >> d.second;
188 }
189
190 // std::map<A, B>
191 template <class A, class B> inline unmarshall &
192 operator>>(unmarshall & u, std::map<A,B> & x) {
193     uint32_t n = u._grab<uint32_t>();
194     x.clear();
195     while (n--)
196         x.emplace(u._grab<std::pair<A,B>>());
197     return u;
198 }
199
200 // std::string
201 inline marshall & operator<<(marshall & m, const string & s) {
202     m << (uint32_t)s.size();
203     m.write(s.data(), s.size());
204     return m;
205 }
206
207 inline unmarshall & operator>>(unmarshall & u, string & s) {
208     uint32_t sz = u._grab<uint32_t>();
209     if (u.ok()) {
210         s.resize(sz);
211         u.read(&s[0], sz);
212     }
213     return u;
214 }
215
216 //
217 // Marshalling for strongly-typed enums
218 //
219
220 template <class E> typename enable_if<std::is_enum<E>::value, marshall>::type &
221 operator<<(marshall & m, E e) {
222     return m << from_enum(e);
223 }
224
225 template <class E> typename enable_if<std::is_enum<E>::value, unmarshall>::type &
226 operator>>(unmarshall & u, E & e) {
227     e = to_enum<E>(u._grab<enum_type_t<E>>());
228     return u;
229 }
230
231 //
232 // Recursive marshalling
233 //
234
235 inline marshall & operator<<(marshall & m, marshall & n) {
236     return m << n.content();
237 }
238
239 inline unmarshall & operator>>(unmarshall & u, unmarshall & v) {
240     v = unmarshall(u._grab<string>(), false);
241     return u;
242 }
243
244 #endif