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