1 /*
2  * Copyright (C) 2009-2017 Codership Oy <info@codership.com>
3  */
4 
5 /*!
6  * @file Helper templates for serialization/unserialization.
7  * As we are usually working on little endian platforms, integer
8  * storage order is little-endian - in other words we use "Galera"
9  * order, which is by default little-endian.
10  *
11  * What is going on down there? Templates are good. However we do
12  * not serialize the value of size_t variable into sizeof(size_t)
13  * bytes. We serialize it into a globally consistent, fixed number
14  * of bytes, regardless of the local size of size_t variable.
15  *
16  * Hence templating by the source variable size should not be used.
17  * Instead there are functions/templates that serialize to an explicit
18  * number of bytes.
19  *
20  * @todo Templates are safe to use with integer types only. Adjust them
21  *       to work also with classes that have special serialization
22  *       routines.
23  * @todo Make buffer serialization functions Buffer class methods.
24  * @todo Alignment issues.
25  */
26 
27 
28 #ifndef GU_SERIALIZE_HPP
29 #define GU_SERIALIZE_HPP
30 
31 #include "gu_exception.hpp"
32 #include "gu_byteswap.hpp"
33 #include "gu_buffer.hpp"
34 #include "gu_macros.hpp"
35 #include "gu_utils.hpp"
36 
37 #include <limits>
38 #include <cstring> // ::memcpy()
39 
40 namespace gu
41 {
42 
43     template <typename T>
serial_size(const T & t)44     inline size_t serial_size(const T& t) { return t.serial_size(); }
45 
46     template <>
serial_size(const uint8_t & b)47     inline size_t serial_size(const uint8_t& b)  { return sizeof(b); }
48 
49     template <>
serial_size(const uint16_t & b)50     inline size_t serial_size(const uint16_t& b) { return sizeof(b); }
51 
52     template <>
serial_size(const uint32_t & b)53     inline size_t serial_size(const uint32_t& b) { return sizeof(b); }
54 
55     template <>
serial_size(const uint64_t & b)56     inline size_t serial_size(const uint64_t& b) { return sizeof(b); }
57 
58     class SerializationException : public Exception
59     {
60     public:
61         SerializationException(size_t ret, size_t buflen);
62     };
63 
64     /*
65      * Non-checking serialization template helpers for cases where buffer size
66      * check is redundant
67      */
68     template <typename TO, typename FROM>
69     inline size_t
serialize_helper(const FROM & f,void * const buf,size_t const offset)70     serialize_helper(const FROM& f, void* const buf, size_t const offset)
71     {
72         GU_COMPILE_ASSERT(std::numeric_limits<TO>::is_integer, not_integer1);
73         GU_COMPILE_ASSERT(std::numeric_limits<FROM>::is_integer, not_integer2);
74         GU_COMPILE_ASSERT(sizeof(FROM) <= sizeof(TO), size_differs);
75 
76         TO const tmp(htog<TO>(f));
77         ::memcpy(ptr_offset(buf, offset), &tmp, sizeof(tmp));
78 
79         return offset + sizeof(tmp);
80     }
81 
82     template <typename FROM, typename TO>
83     inline size_t
unserialize_helper(const void * const buf,size_t const offset,TO & t)84     unserialize_helper(const void* const buf, size_t const offset, TO& t)
85     {
86         GU_COMPILE_ASSERT(std::numeric_limits<TO>::is_integer, not_integer1);
87         GU_COMPILE_ASSERT(std::numeric_limits<FROM>::is_integer, not_integer2);
88         GU_COMPILE_ASSERT(sizeof(FROM) <= sizeof(TO), size_differs);
89 
90         FROM tmp;
91         ::memcpy(&tmp, ptr_offset(buf, offset), sizeof(tmp));
92         t = gtoh<FROM>(tmp);
93 
94         return offset + sizeof(tmp);
95     }
96 
97     /* General serialization templates for numeric types */
98     template <typename FROM>
99     GU_FORCE_INLINE size_t
serialize(const FROM & f,void * const buf,size_t const offset)100     serialize(const FROM& f, void* const buf, size_t const offset)
101     {
102         return serialize_helper<FROM, FROM>(f, buf, offset);
103     }
104 
105     template <typename TO>
106     GU_FORCE_INLINE size_t
unserialize(const void * const buf,size_t const offset,TO & t)107     unserialize(const void* const buf, size_t const offset, TO& t)
108     {
109         return unserialize_helper<TO, TO>(buf, offset, t);
110     }
111 
112     /* The following templates force explicit size serialization/deserialization
113      * at compile stage */
114     template <typename T>
serialize1(const T & t,void * const buf,size_t const offset)115     GU_FORCE_INLINE size_t serialize1(const T&     t,
116                                       void*  const buf,
117                                       size_t const offset)
118     {
119         return serialize_helper<uint8_t>(t, buf, offset);
120     }
121 
122     template <typename T>
unserialize1(const void * const buf,size_t const offset,T & t)123     GU_FORCE_INLINE size_t unserialize1(const void* const buf,
124                                         size_t      const offset,
125                                         T&                t)
126     {
127         return unserialize_helper<uint8_t>(buf, offset, t);
128     }
129 
130     template <typename T>
serialize2(const T & t,void * const buf,size_t const offset)131     GU_FORCE_INLINE size_t serialize2(const T&     t,
132                                       void*  const buf,
133                                       size_t const offset)
134     {
135         return serialize_helper<uint16_t>(t, buf, offset);
136     }
137 
138     template <typename T>
unserialize2(const void * const buf,size_t const offset,T & t)139     GU_FORCE_INLINE size_t unserialize2(const void* const buf,
140                                         size_t      const offset,
141                                         T&                t)
142     {
143         return unserialize_helper<uint16_t>(buf, offset, t);
144     }
145 
146     template <typename T>
serialize4(const T & t,void * const buf,size_t const offset)147     GU_FORCE_INLINE size_t serialize4(const T&     t,
148                                       void*  const buf,
149                                       size_t const offset)
150     {
151         return serialize_helper<uint32_t>(t, buf, offset);
152     }
153 
154     template <typename T>
unserialize4(const void * const buf,size_t const offset,T & t)155     GU_FORCE_INLINE size_t unserialize4(const void* const buf,
156                                         size_t      const offset,
157                                         T&                t)
158     {
159         return unserialize_helper<uint32_t>(buf, offset, t);
160     }
161 
162     template <typename T>
serialize8(const T & t,void * const buf,size_t const offset)163     GU_FORCE_INLINE size_t serialize8(const T&     t,
164                                       void*  const buf,
165                                       size_t const offset)
166     {
167         return serialize_helper<uint64_t>(t, buf, offset);
168     }
169 
170     template <typename T>
unserialize8(const void * const buf,size_t const offset,T & t)171     GU_FORCE_INLINE size_t unserialize8(const void* const buf,
172                                         size_t      const offset,
173                                         T&                t)
174     {
175         return unserialize_helper<uint64_t>(buf, offset, t);
176     }
177 
178     /*
179      * Buffer length checking serialization template helpers
180      */
181     GU_FORCE_INLINE void
check_bounds(size_t need,size_t have)182     check_bounds(size_t need, size_t have)
183     {
184         if (gu_unlikely(need > have))
185             throw SerializationException(need, have);
186     }
187 
188     template <typename TO, typename FROM>
189     inline size_t
serialize_helper(const FROM & f,void * const buf,size_t const buflen,size_t const offset)190     serialize_helper(const FROM& f, void* const buf, size_t const buflen,
191                      size_t const offset)
192     {
193         size_t const check(offset + sizeof(TO));
194 
195         gu_trace(check_bounds(check, buflen));
196 
197         return serialize_helper<TO, FROM>(f, buf, offset);
198     }
199 
200     template <typename FROM, typename TO>
201     inline size_t
unserialize_helper(const void * const buf,size_t const buflen,size_t const offset,TO & t)202     unserialize_helper(const void* const buf, size_t const buflen,
203                        size_t const offset, TO& t)
204     {
205         size_t const check(offset + sizeof(FROM));
206 
207         gu_trace(check_bounds(check, buflen));
208 
209         return unserialize_helper<FROM, TO>(buf, offset, t);
210     }
211 
212     /* General serialization templates for numeric types */
213     template <typename FROM>
214     GU_FORCE_INLINE size_t
serialize(const FROM & f,void * const buf,size_t const buflen,size_t const offset)215     serialize(const FROM& f, void* const buf, size_t const buflen,
216               size_t const offset)
217     {
218         return serialize_helper<FROM, FROM>(f, buf, buflen, offset);
219     }
220 
221     template <typename TO>
222     GU_FORCE_INLINE size_t
unserialize(const void * const buf,size_t const buflen,size_t const offset,TO & t)223     unserialize(const void* const buf, size_t const buflen, size_t const offset,
224                 TO& t)
225     {
226         return unserialize_helper<TO, TO>(buf, buflen, offset, t);
227     }
228 
229     /* The following templates force explicit size serialization/deserialization
230      * at compile stage */
231     template <typename T>
serialize1(const T & t,void * const buf,size_t const buflen,size_t const offset)232     GU_FORCE_INLINE size_t serialize1(const T&     t,
233                                       void*  const buf,
234                                       size_t const buflen,
235                                       size_t const offset)
236     {
237         return serialize_helper<uint8_t>(t, buf, buflen, offset);
238     }
239 
240     template <typename T>
unserialize1(const void * const buf,size_t const buflen,size_t const offset,T & t)241     GU_FORCE_INLINE size_t unserialize1(const void* const buf,
242                                         size_t      const buflen,
243                                         size_t      const offset,
244                                         T&                t)
245     {
246         return unserialize_helper<uint8_t>(buf, buflen, offset, t);
247     }
248 
249     template <typename T>
serialize2(const T & t,void * const buf,size_t const buflen,size_t const offset)250     GU_FORCE_INLINE size_t serialize2(const T&     t,
251                                       void*  const buf,
252                                       size_t const buflen,
253                                       size_t const offset)
254     {
255         return serialize_helper<uint16_t>(t, buf, buflen, offset);
256     }
257 
258     template <typename T>
unserialize2(const void * const buf,size_t const buflen,size_t const offset,T & t)259     GU_FORCE_INLINE size_t unserialize2(const void* const buf,
260                                         size_t      const buflen,
261                                         size_t      const offset,
262                                         T&                t)
263     {
264         return unserialize_helper<uint16_t>(buf, buflen, offset, t);
265     }
266 
267     template <typename T>
serialize4(const T & t,void * const buf,size_t const buflen,size_t const offset)268     GU_FORCE_INLINE size_t serialize4(const T&     t,
269                                       void*  const buf,
270                                       size_t const buflen,
271                                       size_t const offset)
272     {
273         return serialize_helper<uint32_t>(t, buf, buflen, offset);
274     }
275 
276     template <typename T>
unserialize4(const void * const buf,size_t const buflen,size_t const offset,T & t)277     GU_FORCE_INLINE size_t unserialize4(const void* const buf,
278                                         size_t      const buflen,
279                                         size_t      const offset,
280                                         T&                t)
281     {
282         return unserialize_helper<uint32_t>(buf, buflen, offset, t);
283     }
284 
285     template <typename T>
serialize8(T const t,void * const buf,size_t const buflen,size_t const offset)286     GU_FORCE_INLINE size_t serialize8(T   const t,
287                                       void*  const buf,
288                                       size_t const buflen,
289                                       size_t const offset)
290     {
291         return serialize_helper<uint64_t>(t, buf, buflen, offset);
292     }
293 
294     template <typename T>
unserialize8(const void * const buf,size_t const buflen,size_t const offset,T & t)295     GU_FORCE_INLINE size_t unserialize8(const void* const buf,
296                                         size_t      const buflen,
297                                         size_t      const offset,
298                                         T&                t)
299     {
300         return unserialize_helper<uint64_t>(buf, buflen, offset, t);
301     }
302 
303     /*
304      * Templates to serialize arbitrary length buffers
305      */
306     class RepresentationException : public Exception
307     {
308     public:
309         RepresentationException(size_t need, size_t have);
310     };
311 
312     template <typename ST>
serial_size_helper(const Buffer & sb)313     inline size_t serial_size_helper(const Buffer& sb)
314     {
315         GU_COMPILE_ASSERT(std::numeric_limits<ST>::is_integer, must_be_integer);
316 
317         if (gu_unlikely(sb.size() > std::numeric_limits<ST>::max()))
318             throw RepresentationException(sb.size(), sizeof(ST));
319 
320         return sizeof(ST) + sb.size();
321     }
322 
serial_size1(const Buffer & sb)323     GU_FORCE_INLINE size_t serial_size1(const Buffer& sb)
324     {
325         return serial_size_helper<uint8_t>(sb);
326     }
327 
serial_size2(const Buffer & sb)328     GU_FORCE_INLINE size_t serial_size2(const Buffer& sb)
329     {
330         return serial_size_helper<uint16_t>(sb);
331     }
332 
serial_size4(const Buffer & sb)333     GU_FORCE_INLINE size_t serial_size4(const Buffer& sb)
334     {
335         return serial_size_helper<uint32_t>(sb);
336     }
337 
serial_size8(const Buffer & sb)338     GU_FORCE_INLINE size_t serial_size8(const Buffer& sb)
339     {
340         return serial_size_helper<uint64_t>(sb);
341     }
342 
343     template <typename ST>
serialize_helper(const Buffer & b,void * const buf,size_t const buflen,size_t offset)344     inline size_t serialize_helper(const Buffer& b,
345                                    void*   const buf,
346                                    size_t  const buflen,
347                                    size_t        offset)
348     {
349         size_t const ret(offset + serial_size_helper<ST>(b));
350 
351         gu_trace(check_bounds(ret, buflen));
352 
353         offset = serialize_helper<ST>(static_cast<ST>(b.size()),
354                                       buf, buflen, offset);
355 
356         // can't use void* in std::copy()
357         byte_t* const ptr(static_cast<byte_t*>(buf));
358         std::copy(b.begin(), b.end(), ptr + offset);
359         return ret;
360     }
361 
362     template <typename ST>
unserialize_helper(const void * const buf,size_t const buflen,size_t offset,Buffer & b)363     inline size_t unserialize_helper(const void* const buf,
364                                      size_t      const buflen,
365                                      size_t            offset,
366                                      Buffer&           b)
367     {
368         GU_COMPILE_ASSERT(std::numeric_limits<ST>::is_integer, must_be_integer);
369         ST len(0);
370         size_t ret(offset + sizeof(len));
371 
372         gu_trace(check_bounds(ret, buflen));
373 
374         offset = unserialize_helper<ST>(buf, buflen, offset, len);
375         ret += len;
376 
377         gu_trace(check_bounds(ret, buflen));
378 
379         b.resize(len);
380 
381         // can't use void* in std::copy()
382         const byte_t* const ptr(static_cast<const byte_t*>(buf));
383         std::copy(ptr + offset, ptr + ret, b.begin());
384 
385         return ret;
386     }
387 
serialize1(const Buffer & b,void * const buf,size_t const buflen,size_t const offset)388     GU_FORCE_INLINE size_t serialize1(const Buffer& b,
389                                       void*   const buf,
390                                       size_t  const buflen,
391                                       size_t  const offset)
392     {
393         return serialize_helper<uint8_t>(b, buf, buflen, offset);
394     }
395 
unserialize1(const void * const buf,size_t const buflen,size_t const offset,Buffer & b)396     GU_FORCE_INLINE size_t unserialize1(const void* const buf,
397                                         size_t      const buflen,
398                                         size_t      const offset,
399                                         Buffer&           b)
400     {
401         return unserialize_helper<uint8_t>(buf, buflen, offset, b);
402     }
403 
serialize2(const Buffer & b,void * const buf,size_t const buflen,size_t const offset)404     GU_FORCE_INLINE size_t serialize2(const Buffer& b,
405                                       void*   const buf,
406                                       size_t  const buflen,
407                                       size_t  const offset)
408     {
409         return serialize_helper<uint16_t>(b, buf, buflen, offset);
410     }
411 
unserialize2(const void * const buf,size_t const buflen,size_t const offset,Buffer & b)412     GU_FORCE_INLINE size_t unserialize2(const void* const buf,
413                                         size_t      const buflen,
414                                         size_t      const offset,
415                                         Buffer&           b)
416     {
417         return unserialize_helper<uint16_t>(buf, buflen, offset, b);
418     }
419 
serialize4(const Buffer & b,void * const buf,size_t const buflen,size_t const offset)420     GU_FORCE_INLINE size_t serialize4(const Buffer& b,
421                                       void*   const buf,
422                                       size_t  const buflen,
423                                       size_t  const offset)
424     {
425         return serialize_helper<uint32_t>(b, buf, buflen, offset);
426     }
427 
unserialize4(const void * const buf,size_t const buflen,size_t const offset,Buffer & b)428     GU_FORCE_INLINE size_t unserialize4(const void* const buf,
429                                         size_t      const buflen,
430                                         size_t      const offset,
431                                         Buffer&           b)
432     {
433         return unserialize_helper<uint32_t>(buf, buflen, offset, b);
434     }
435 
serialize8(const Buffer & b,void * const buf,size_t const buflen,size_t const offset)436     GU_FORCE_INLINE size_t serialize8(const Buffer& b,
437                                       void*   const buf,
438                                       size_t  const buflen,
439                                       size_t  const offset)
440     {
441         return serialize_helper<uint64_t>(b, buf, buflen, offset);
442     }
443 
unserialize8(const void * const buf,size_t const buflen,size_t const offset,Buffer & b)444     GU_FORCE_INLINE size_t unserialize8(const void* const buf,
445                                         size_t      const buflen,
446                                         size_t      const offset,
447                                         Buffer&           b)
448     {
449         return unserialize_helper<uint64_t>(buf, buflen, offset, b);
450     }
451 
452 } // namespace gu
453 
454 #endif // GU_SERIALIZE_HPP
455