// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- // vim: ts=8 sw=2 smarttab /* * Ceph - scalable distributed file system * * Copyright (C) 2016 Allen Samuels * * This is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License version 2.1, as published by the Free Software * Foundation. See file COPYING. * */ // If you #include "include/encoding.h" you get the old-style *and* // the new-style definitions. (The old-style needs denc_traits<> in // order to disable the container helpers when new-style traits are // present.) // You can also just #include "include/denc.h" and get only the // new-style helpers. The eventual goal is to drop the legacy // definitions. #ifndef _ENC_DEC_H #define _ENC_DEC_H #include #include #include #include #include #include #include #include #include #include #include #include "include/assert.h" // boost clobbers this #include "include/intarith.h" #include "include/int_types.h" #include "include/memory.h" #include "buffer.h" #include "byteorder.h" template struct denc_traits { static constexpr bool supported = false; static constexpr bool featured = false; static constexpr bool bounded = false; static constexpr bool need_contiguous = true; }; // hack for debug only; FIXME //#include //using std::cout; // Define this to compile in a dump of all encoded objects to disk to // populate ceph-object-corpus. Note that there is an almost // identical implementation in encoding.h, but you only need to define // ENCODE_DUMP_PATH here. // // See src/test/encoding/generate-corpus-objects.sh. // //#define ENCODE_DUMP_PATH /tmp/something #ifdef ENCODE_DUMP_PATH # include # include # include # include # define ENCODE_STR(x) #x # define ENCODE_STRINGIFY(x) ENCODE_STR(x) # define DENC_DUMP_PRE(Type) \ char *__denc_dump_pre = p.get_pos(); // this hackery with bits below is just to get a semi-reasonable // distribution across time. it is somewhat exponential but not // quite. # define DENC_DUMP_POST(Type) \ do { \ static int i = 0; \ i++; \ int bits = 0; \ for (unsigned t = i; t; bits++) \ t &= t - 1; \ if (bits > 2) \ break; \ char fn[PATH_MAX]; \ snprintf(fn, sizeof(fn), \ ENCODE_STRINGIFY(ENCODE_DUMP_PATH) "/%s__%d.%x", #Type, \ getpid(), i++); \ int fd = ::open(fn, O_WRONLY|O_TRUNC|O_CREAT, 0644); \ if (fd >= 0) { \ size_t len = p.get_pos() - __denc_dump_pre; \ int r = ::write(fd, __denc_dump_pre, len); \ (void)r; \ ::close(fd); \ } \ } while (0) #else # define DENC_DUMP_PRE(Type) # define DENC_DUMP_POST(Type) #endif /* top level level functions look like so ====================================== inline void denc(const T& o, size_t& p, uint64_t features=0); inline void denc(const T& o, buffer::list::contiguous_appender& p, uint64_t features=0); inline void denc(T& o, buffer::ptr::iterator& p, uint64_t features=0); or (for featured objects) inline void denc(const T& o, size_t& p, uint64_t features); inline void denc(const T& o, buffer::list::contiguous_appender& p, uint64_t features); inline void denc(T& o, buffer::ptr::iterator& p, uint64_t features); - These are symmetrical, so that they can be used from the magic DENC method of writing the bound_encode/encode/decode methods all in one go; they differ only in the type of p. - These are automatically fabricated via a template that calls into the denc_traits<> methods (see below), provided denc_traits::supported is defined and true. They never need to be written explicitly. static denc_traits<> definitions look like so ============================================= template<> struct denc_traits { static constexpr bool supported = true; static constexpr bool bounded = false; static constexpr bool featured = false; static constexpr bool need_contiguous = true; static void bound_encode(const T &o, size_t& p, uint64_t f=0); static void encode(const T &o, buffer::list::contiguous_appender& p, uint64_t f=0); static void decode(T& o, buffer::ptr::iterator &p, uint64_t f=0); }; or (for featured objects) template<> struct denc_traits { static constexpr bool supported = true; static constexpr bool bounded = false; static constexpr bool featured = true; static constexpr bool need_contiguous = true; static void bound_encode(const T &o, size_t& p, uint64_t f); static void encode(const T &o, buffer::list::contiguous_appender& p, uint64_t f); static void decode(T& o, buffer::ptr::iterator &p, uint64_t f=0); }; - denc_traits is normally declared via the WRITE_CLASS_DENC(type) macro, which is used in place of the old-style WRITE_CLASS_ENCODER(type) macro. There are _FEATURED and _BOUNDED variants. The class traits simply call into class methods of the same name (see below). - denc_traits can also be written explicitly for some type to indicate how it should be encoded. This is the "source of truth" for how a type is encoded. - denc_traits are declared for the base integer types, string, bufferptr, and bufferlist base types. - denc_traits>-like traits are declared for standard container types. class methods look like so ========================== void bound_encode(size_t& p) const; void encode(buffer::list::contiguous_appender& p) const; void decode(buffer::ptr::iterator &p); or (for featured objects) void bound_encode(size_t& p, uint64_t f) const; void encode(buffer::list::contiguous_appender& p, uint64_t f) const; void decode(buffer::ptr::iterator &p); - These are normally invoked by the denc_traits<> methods that are declared via WRITE_CLASS_DENC, although you can also invoke them explicitly in your code. - These methods are optimised for contiguous buffer, but denc() will try rebuild a contigous one if the decoded bufferlist is segmented. If you are concerned about the cost, you might want to define yet another method: void decode(buffer::list::iterator &p); - These can be defined either explicitly (as above), or can be "magically" defined all in one go using the DENC macro and DENC_{START,FINISH} helpers (which work like the legacy {ENCODE,DECODE}_{START,FINISH} macros): class foo_t { ... DENC(foo_t, v, p) { DENC_START(1, 1, p); denc(v.foo, p); denc(v.bar, p); denc(v.baz, p); DENC_FINISH(p); } ... }; WRITE_CLASS_DENC(foo_t) */ // --------------------------------------------------------------------- // raw types namespace _denc { template struct is_any_of : std::false_type {}; template struct is_any_of : std::conditional< std::is_same::value, std::true_type, is_any_of>::type {}; } template struct denc_traits< T, typename std::enable_if< _denc::is_any_of::value>::type> { static constexpr bool supported = true; static constexpr bool featured = false; static constexpr bool bounded = true; static constexpr bool need_contiguous = false; static void bound_encode(const T &o, size_t& p, uint64_t f=0) { p += sizeof(T); } static void encode(const T &o, buffer::list::contiguous_appender& p, uint64_t f=0) { p.append((const char*)&o, sizeof(o)); } static void decode(T& o, buffer::ptr::iterator &p, uint64_t f=0) { o = *(T *)p.get_pos_add(sizeof(o)); } static void decode(T& o, buffer::list::iterator &p) { p.copy(sizeof(T), reinterpret_cast(&o)); } }; // ----------------------------------------------------------------------- // integer types // itype == internal type // otype == external type, i.e., the type on the wire // NOTE: the overload resolution ensures that the legacy encode/decode methods // defined for int types is prefered to the ones defined using the specialized // template, and hence get selected. This machinary prevents these these from // getting glued into the legacy encode/decode methods; the overhead of setting // up a contiguous_appender etc is likely to be slower. namespace _denc { template struct ExtType { using type = void; }; template struct ExtType< T, typename std::enable_if::value || std::is_same::value>::type> { using type = __le16; }; template struct ExtType< T, typename std::enable_if::value || std::is_same::value>::type> { using type = __le32; }; template struct ExtType< T, typename std::enable_if::value || std::is_same::value>::type> { using type = __le64; }; template<> struct ExtType { using type = uint8_t; }; } // namespace _denc template struct denc_traits< T, typename std::enable_if::type>::value>::type> { static constexpr bool supported = true; static constexpr bool featured = false; static constexpr bool bounded = true; static constexpr bool need_contiguous = false; using etype = typename _denc::ExtType::type; static void bound_encode(const T &o, size_t& p, uint64_t f=0) { p += sizeof(etype); } static void encode(const T &o, buffer::list::contiguous_appender& p, uint64_t f=0) { *(etype *)p.get_pos_add(sizeof(etype)) = o; } static void decode(T& o, buffer::ptr::iterator &p, uint64_t f=0) { o = *(etype*)p.get_pos_add(sizeof(etype)); } static void decode(T& o, buffer::list::iterator &p) { etype e; p.copy(sizeof(etype), reinterpret_cast(&e)); o = e; } }; // varint // // high bit of each byte indicates another byte follows. template inline void denc_varint(T v, size_t& p) { p += sizeof(T) + 1; } template inline void denc_varint(T v, bufferlist::contiguous_appender& p) { uint8_t byte = v & 0x7f; v >>= 7; while (v) { byte |= 0x80; *(__u8*)p.get_pos_add(1) = byte; byte = (v & 0x7f); v >>= 7; } *(__u8*)p.get_pos_add(1) = byte; } template inline void denc_varint(T& v, bufferptr::iterator& p) { uint8_t byte = *(__u8*)p.get_pos_add(1); v = byte & 0x7f; int shift = 7; while (byte & 0x80) { byte = *(__u8*)p.get_pos_add(1); v |= (T)(byte & 0x7f) << shift; shift += 7; } } // signed varint encoding // // low bit = 1 = negative, 0 = positive // high bit of every byte indicates whether another byte follows. inline void denc_signed_varint(int64_t v, size_t& p) { p += sizeof(v) + 2; } inline void denc_signed_varint(int64_t v, bufferlist::contiguous_appender& p) { if (v < 0) { v = (-v << 1) | 1; } else { v <<= 1; } denc_varint(v, p); } template inline void denc_signed_varint(T& v, bufferptr::iterator& p) { int64_t i = 0; denc_varint(i, p); if (i & 1) { v = -(i >> 1); } else { v = i >> 1; } } // varint + lowz encoding // // first(low) 2 bits = how many low zero bits (nibbles) // high bit of each byte = another byte follows // (so, 5 bits data in first byte, 7 bits data thereafter) inline void denc_varint_lowz(uint64_t v, size_t& p) { p += sizeof(v) + 2; } inline void denc_varint_lowz(uint64_t v, bufferlist::contiguous_appender& p) { int lowznib = v ? (ctz(v) / 4) : 0; if (lowznib > 3) lowznib = 3; v >>= lowznib * 4; v <<= 2; v |= lowznib; denc_varint(v, p); } template inline void denc_varint_lowz(T& v, bufferptr::iterator& p) { uint64_t i = 0; denc_varint(i, p); int lowznib = (i & 3); i >>= 2; i <<= lowznib * 4; v = i; } // signed varint + lowz encoding // // first low bit = 1 for negative, 0 for positive // next 2 bits = how many low zero bits (nibbles) // high bit of each byte = another byte follows // (so, 4 bits data in first byte, 7 bits data thereafter) inline void denc_signed_varint_lowz(int64_t v, size_t& p) { p += sizeof(v) + 2; } inline void denc_signed_varint_lowz(int64_t v, bufferlist::contiguous_appender& p) { bool negative = false; if (v < 0) { v = -v; negative = true; } unsigned lowznib = v ? (ctz(v) / 4) : 0u; if (lowznib > 3) lowznib = 3; v >>= lowznib * 4; v <<= 3; v |= lowznib << 1; v |= (int)negative; denc_varint(v, p); } template inline void denc_signed_varint_lowz(T& v, bufferptr::iterator& p) { int64_t i = 0; denc_varint(i, p); int lowznib = (i & 6) >> 1; if (i & 1) { i >>= 3; i <<= lowznib * 4; v = -i; } else { i >>= 3; i <<= lowznib * 4; v = i; } } // LBA // // first 1-3 bits = how many low zero bits // *0 = 12 (common 4 K alignment case) // *01 = 16 // *011 = 20 // *111 = byte // then 28-30 bits of data // then last bit = another byte follows // high bit of each subsequent byte = another byte follows inline void denc_lba(uint64_t v, size_t& p) { p += sizeof(v) + 2; } inline void denc_lba(uint64_t v, bufferlist::contiguous_appender& p) { int low_zero_nibbles = v ? (int)(ctz(v) / 4) : 0; int pos; uint32_t word; int t = low_zero_nibbles - 3; if (t < 0) { pos = 3; word = 0x7; } else if (t < 3) { v >>= (low_zero_nibbles * 4); pos = t + 1; word = (1 << t) - 1; } else { v >>= 20; pos = 3; word = 0x3; } word |= (v << pos) & 0x7fffffff; v >>= 31 - pos; if (!v) { *(__le32*)p.get_pos_add(sizeof(uint32_t)) = word; return; } word |= 0x80000000; *(__le32*)p.get_pos_add(sizeof(uint32_t)) = word; uint8_t byte = v & 0x7f; v >>= 7; while (v) { byte |= 0x80; *(__u8*)p.get_pos_add(1) = byte; byte = (v & 0x7f); v >>= 7; } *(__u8*)p.get_pos_add(1) = byte; } inline void denc_lba(uint64_t& v, bufferptr::iterator& p) { uint32_t word = *(__le32*)p.get_pos_add(sizeof(uint32_t)); int shift; switch (word & 7) { case 0: case 2: case 4: case 6: v = (uint64_t)(word & 0x7ffffffe) << (12 - 1); shift = 12 + 30; break; case 1: case 5: v = (uint64_t)(word & 0x7ffffffc) << (16 - 2); shift = 16 + 29; break; case 3: v = (uint64_t)(word & 0x7ffffff8) << (20 - 3); shift = 20 + 28; break; case 7: v = (uint64_t)(word & 0x7ffffff8) >> 3; shift = 28; } uint8_t byte = word >> 24; while (byte & 0x80) { byte = *(__u8*)p.get_pos_add(1); v |= (uint64_t)(byte & 0x7f) << shift; shift += 7; } } // --------------------------------------------------------------------- // denc top-level methods that call into denc_traits methods template> inline typename std::enable_if::type denc( const T& o, size_t& p, uint64_t f=0) { traits::bound_encode(o, p); } template> inline typename std::enable_if::type denc( const T& o, size_t& p, uint64_t f) { traits::bound_encode(o, p, f); } template> inline typename std::enable_if::type denc( const T& o, buffer::list::contiguous_appender& p, uint64_t features=0) { traits::encode(o, p); } template> inline typename std::enable_if::type denc( const T& o, buffer::list::contiguous_appender& p, uint64_t features) { traits::encode(o, p, features); } template> inline typename std::enable_if::type denc( T& o, buffer::ptr::iterator& p, uint64_t features=0) { traits::decode(o, p); } template> inline typename std::enable_if::type denc( T& o, buffer::ptr::iterator& p, uint64_t features=0) { traits::decode(o, p, features); } namespace _denc { template struct has_legacy_denc : std::false_type {}; template struct has_legacy_denc() .decode(std::declval()))> : std::true_type { static void decode(T& v, bufferlist::iterator& p) { v.decode(p); } }; template struct has_legacy_denc::need_contiguous>::type> : std::true_type { static void decode(T& v, bufferlist::iterator& p) { denc_traits::decode(v, p); } }; } template, typename has_legacy_denc=_denc::has_legacy_denc> inline typename std::enable_if::type denc( T& o, buffer::list::iterator& p) { has_legacy_denc::decode(o, p); } // --------------------------------------------------------------------- // base types and containers // // std::string // template struct denc_traits,A>> { private: using value_type = std::basic_string,A>; public: static constexpr bool supported = true; static constexpr bool featured = false; static constexpr bool bounded = false; static constexpr bool need_contiguous = false; static void bound_encode(const value_type& s, size_t& p, uint64_t f=0) { p += sizeof(uint32_t) + s.size(); } static void encode(const value_type& s, buffer::list::contiguous_appender& p, uint64_t f=0) { ::denc((uint32_t)s.size(), p); memcpy(p.get_pos_add(s.size()), s.data(), s.size()); } static void decode(value_type& s, buffer::ptr::iterator& p, uint64_t f=0) { uint32_t len; ::denc(len, p); decode_nohead(len, s, p); } static void decode(value_type& s, buffer::list::iterator& p) { uint32_t len; ::denc(len, p); s.clear(); p.copy(len, s); } static void decode_nohead(size_t len, value_type& s, buffer::ptr::iterator& p) { s.clear(); if (len) { s.append(p.get_pos_add(len), len); } } static void encode_nohead(const value_type& s, buffer::list::contiguous_appender& p) { p.append(s.data(), s.length()); } }; // // bufferptr // template<> struct denc_traits { static constexpr bool supported = true; static constexpr bool featured = false; static constexpr bool bounded = false; static constexpr bool need_contiguous = false; static void bound_encode(const bufferptr& v, size_t& p, uint64_t f=0) { p += sizeof(uint32_t) + v.length(); } static void encode(const bufferptr& v, buffer::list::contiguous_appender& p, uint64_t f=0) { ::denc((uint32_t)v.length(), p); p.append(v); } static void decode(bufferptr& v, buffer::ptr::iterator& p, uint64_t f=0) { uint32_t len; ::denc(len, p); v = p.get_ptr(len); } static void decode(bufferptr& v, buffer::list::iterator& p) { uint32_t len; ::denc(len, p); bufferlist s; p.copy(len, s); if (len) { if (s.get_num_buffers() == 1) v = s.front(); else v = buffer::copy(s.c_str(), s.length()); } } }; // // bufferlist // template<> struct denc_traits { static constexpr bool supported = true; static constexpr bool featured = false; static constexpr bool bounded = false; static constexpr bool need_contiguous = false; static void bound_encode(const bufferlist& v, size_t& p, uint64_t f=0) { p += sizeof(uint32_t) + v.length(); } static void encode(const bufferlist& v, buffer::list::contiguous_appender& p, uint64_t f=0) { ::denc((uint32_t)v.length(), p); p.append(v); } static void decode(bufferlist& v, buffer::ptr::iterator& p, uint64_t f=0) { uint32_t len; ::denc(len, p); v.clear(); v.push_back(p.get_ptr(len)); } static void decode(bufferlist& v, buffer::list::iterator& p) { uint32_t len; ::denc(len, p); v.clear(); p.copy(len, v); } static void encode_nohead(const bufferlist& v, buffer::list::contiguous_appender& p) { p.append(v); } static void decode_nohead(size_t len, bufferlist& v, buffer::ptr::iterator& p) { v.clear(); if (len) { v.append(p.get_ptr(len)); } } }; // // std::pair // template struct denc_traits< std::pair, typename std::enable_if::supported && denc_traits::supported>::type> { typedef denc_traits a_traits; typedef denc_traits b_traits; static constexpr bool supported = true; static constexpr bool featured = a_traits::featured || b_traits::featured ; static constexpr bool bounded = a_traits::bounded && b_traits::bounded; static constexpr bool need_contiguous = (a_traits::need_contiguous || b_traits::need_contiguous); template static typename std::enable_if::type bound_encode(const std::pair& v, size_t& p) { denc(v.first, p); denc(v.second, p); } template static typename std::enable_if::type bound_encode(const std::pair& v, size_t& p, uint64_t f) { denc(v.first, p, f); denc(v.second, p, f); } template static typename std::enable_if::type encode(const std::pair& v, bufferlist::contiguous_appender& p) { denc(v.first, p); denc(v.second, p); } template static typename std::enable_if::type encode(const std::pair& v, bufferlist::contiguous_appender& p, uint64_t f) { denc(v.first, p, f); denc(v.second, p, f); } static void decode(std::pair& v, buffer::ptr::iterator& p, uint64_t f=0) { denc(v.first, p, f); denc(v.second, p, f); } template static typename std::enable_if::type decode(std::pair& v, buffer::list::iterator& p, uint64_t f = 0) { denc(v.first, p); denc(v.second, p); } }; namespace _denc { template class C, typename Details, typename ...Ts> struct container_base { private: using container = C; using T = typename Details::T; public: using traits = denc_traits; static constexpr bool supported = true; static constexpr bool featured = traits::featured; static constexpr bool bounded = false; static constexpr bool need_contiguous = traits::need_contiguous; template static typename std::enable_if::type bound_encode(const container& s, size_t& p) { p += sizeof(uint32_t); for (const T& e : s) { denc(e, p); } } template static typename std::enable_if::type bound_encode(const container& s, size_t& p) { size_t elem_size = 0; p += sizeof(uint32_t); if (!s.empty()) { // STL containers use weird element types like std::pair; // cast to something we have denc_traits for. denc(static_cast(*s.begin()), elem_size); p += sizeof(uint32_t) + elem_size * s.size(); } } template static typename std::enable_if::type bound_encode(const container& s, size_t& p, uint64_t f) { p += sizeof(uint32_t); for (const T& e : s) { denc(e, p, f); } } template static typename std::enable_if::type bound_encode(const container& s, size_t& p, uint64_t f) { size_t elem_size = 0; p += sizeof(uint32_t); if (!s.empty()) { // STL containers use weird element types like std::pair; // cast to something we have denc_traits for. denc(static_cast(*s.begin()), elem_size, f); p += elem_size * s.size(); } } template static typename std::enable_if::type encode(const container& s, buffer::list::contiguous_appender& p) { denc((uint32_t)s.size(), p); encode_nohead(s, p); } template static typename std::enable_if::type encode(const container& s, buffer::list::contiguous_appender& p, uint64_t f) { denc((uint32_t)s.size(), p); encode_nohead(s, p, f); } static void decode(container& s, buffer::ptr::iterator& p, uint64_t f = 0) { uint32_t num; denc(num, p); decode_nohead(num, s, p, f); } template static typename std::enable_if::type decode(container& s, buffer::list::iterator& p) { uint32_t num; denc(num, p); decode_nohead(num, s, p); } // nohead template static typename std::enable_if::type encode_nohead(const container& s, buffer::list::contiguous_appender& p) { for (const T& e : s) { denc(e, p); } } template static typename std::enable_if::type encode_nohead(const container& s, buffer::list::contiguous_appender& p, uint64_t f) { for (const T& e : s) { denc(e, p, f); } } static void decode_nohead(size_t num, container& s, buffer::ptr::iterator& p, uint64_t f=0) { s.clear(); Details::reserve(s, num); while (num--) { T t; denc(t, p, f); Details::insert(s, std::move(t)); } } template static typename std::enable_if::type decode_nohead(size_t num, container& s, buffer::list::iterator& p) { s.clear(); Details::reserve(s, num); while (num--) { T t; denc(t, p); Details::insert(s, std::move(t)); } } }; template class container_has_reserve { template struct SFINAE_match; template static std::true_type test(SFINAE_match*); template static std::false_type test(...); public: static constexpr bool value = decltype( test>(0))::value; }; template::value> struct reserve_switch; template struct reserve_switch { static void reserve(Container& c, size_t s) { c.reserve(s); } }; template struct reserve_switch { static void reserve(Container& c, size_t s) {} }; template struct container_details_base : public reserve_switch { using T = typename Container::value_type; }; template struct pushback_details : public container_details_base { template static void insert(Container& c, Args&& ...args) { c.emplace_back(std::forward(args)...); } }; } template struct denc_traits< std::list, typename std::enable_if::supported>::type> : public _denc::container_base>, T, Ts...> {}; template struct denc_traits< std::vector, typename std::enable_if::supported>::type> : public _denc::container_base>, T, Ts...> {}; namespace _denc { template struct setlike_details : public container_details_base { using T = typename Container::value_type; template static void insert(Container& c, Args&& ...args) { c.emplace_hint(c.cend(), std::forward(args)...); } }; } template struct denc_traits< std::set, typename std::enable_if::supported>::type> : public _denc::container_base>, T, Ts...> {}; template struct denc_traits< boost::container::flat_set, typename std::enable_if::supported>::type> : public _denc::container_base< boost::container::flat_set, _denc::setlike_details>, T, Ts...> {}; namespace _denc { template struct maplike_details : public container_details_base { using T = std::pair; template static void insert(Container& c, Args&& ...args) { c.emplace_hint(c.cend(), std::forward(args)...); } }; } template struct denc_traits< std::map, typename std::enable_if::supported && denc_traits::supported>::type> : public _denc::container_base>, A, B, Ts...> {}; template struct denc_traits< boost::container::flat_map, typename std::enable_if::supported && denc_traits::supported>::type> : public _denc::container_base< boost::container::flat_map, _denc::maplike_details>, A, B, Ts...> {}; template struct denc_traits< std::array, typename std::enable_if::supported>::type> { private: using container = std::array; public: using traits = denc_traits; static constexpr bool supported = true; static constexpr bool featured = traits::featured; static constexpr bool bounded = traits::bounded; static constexpr bool need_contiguous = traits::need_contiguous; template static typename std::enable_if::type bound_encode(const container& s, size_t& p) { for (const auto& e : s) denc(e, p); } template static typename std::enable_if::type bound_encode(const container& s, size_t& p) { size_t elem_size = 0; denc(*s.begin(), elem_size); p += elem_size * N; } template static typename std::enable_if::type bound_encode(const container& s, size_t& p, uint64_t f) { for (const auto& e : s) denc(e, p, f); } template static typename std::enable_if::type bound_encode(const container& s, size_t& p, uint64_t f) { size_t elem_size = 0; p += sizeof(uint32_t); if (!s.empty()) { denc(*s.begin(), elem_size, f); p += elem_size * s.size(); } } template static typename std::enable_if::type encode(const container& s, buffer::list::contiguous_appender& p) { for (const auto& e : s) denc(e, p); } template static typename std::enable_if::type encode(const container& s, buffer::list::contiguous_appender& p, uint64_t f) { for (const auto& e : s) denc(e, p, f); } static void decode(container& s, buffer::ptr::iterator& p, uint64_t f = 0) { for (auto& e : s) denc(e, p, f); } template static typename std::enable_if::type decode(container& s, buffer::list::iterator& p) { for (auto& e : s) { denc(e, p); } } }; namespace _denc { template struct indices {}; template struct build_indices_helper; template struct build_indices_helper { using type = indices; }; template struct build_indices_helper { using type = typename build_indices_helper::type; }; template struct build_indices { using type = typename build_indices_helper::type; }; template<> struct build_indices<0> { using type = indices<>; }; template<> struct build_indices<1> { using type = indices<0>; }; template using build_indices_t = typename build_indices::type; template struct tuple_traits; template struct tuple_traits { static constexpr bool supported = (denc_traits::supported && tuple_traits::supported); static constexpr bool bounded = (denc_traits::bounded && tuple_traits::bounded); static constexpr bool featured = (denc_traits::featured || tuple_traits::featured); static constexpr bool need_contiguous = (denc_traits::need_contiguous || tuple_traits::need_contiguous); }; template<> struct tuple_traits<> { static constexpr bool supported = true; static constexpr bool bounded = true; static constexpr bool featured = false; static constexpr bool need_contiguous = false; }; } template struct denc_traits< std::tuple, typename std::enable_if<_denc::tuple_traits::supported>::type> { private: static_assert(sizeof...(Ts) > 0, "Zero-length tuples are not supported."); using container = std::tuple; template static void bound_encode_helper_nfnb(const T& s, size_t& p, _denc::indices) { denc(std::get(s), p); bound_encode_helper_nfnb(s, p, _denc::indices{}); } template static void bound_encode_helper_nfnb(const T& s, size_t& p, _denc::indices) { denc(std::get(s), p); } template static void bound_encode_helper_nfb(const T& s, size_t& p, _denc::indices) { denc(std::get(s), p); bound_encode_helper_nfb(s, p, _denc::indices{}); } template static void bound_encode_helper_nfb(const T& s, size_t& p, _denc::indices) { denc(std::get(s), p); } template static void bound_encode_helper_fnb(const T& s, size_t& p, uint64_t f, _denc::indices) { denc(std::get(s), p, f); bound_encode_helper_fnb(s, p, f, _denc::indices{}); } template static void bound_encode_helper_fnb(const T& s, size_t& p, uint64_t f, _denc::indices) { denc(std::get(s), p, f); } template static void bound_encode_helper_fb(const T& s, size_t& p, uint64_t f, _denc::indices) { denc(std::get(s), p); bound_encode_helper_fb(s, p, f, _denc::indices{}); } template static void bound_encode_helper_fb(const T& s, size_t& p, uint64_t f, _denc::indices) { denc(std::get(s), p); } template static void encode_helper_nf(const T& s, buffer::list::contiguous_appender& p, _denc::indices) { denc(std::get(s), p); encode_helper_nf(s, p, _denc::indices{}); } template static void encode_helper_nf(const T& s, buffer::list::contiguous_appender& p, _denc::indices) { denc(std::get(s), p); } template static void encode_helper_f(const T& s, buffer::list::contiguous_appender& p, uint64_t f, _denc::indices) { denc(std::get(s), p, f); encode_helper_nf(s, p, f, _denc::indices{}); } template static void encode_helper_f(const T& s, buffer::list::contiguous_appender& p, uint64_t f, _denc::indices) { denc(std::get(s), p, f); } template static void decode_helper(T& s, buffer::ptr::iterator& p, _denc::indices) { denc(std::get(s), p); decode_helper(s, p, _denc::indices{}); } template static void decode_helper(T& s, buffer::ptr::iterator& p, _denc::indices) { denc(std::get(s), p); } template static void decode_helper(T& s, buffer::list::iterator& p, _denc::indices) { denc(std::get(s), p); decode_helper(s, p, _denc::indices{}); } template static void decode_helper(T& s, buffer::list::iterator& p, _denc::indices) { denc(std::get(s), p); } public: using traits = _denc::tuple_traits; static constexpr bool supported = true; static constexpr bool featured = traits::featured; static constexpr bool bounded = traits::bounded; static constexpr bool need_contiguous = traits::need_contiguous; template static typename std::enable_if::type bound_encode(const container& s, size_t& p) { bound_encode_helper_nfnb(s, p, _denc::build_indices_t{}); } template static typename std::enable_if::type bound_encode(const container& s, size_t& p) { bound_encode_helper_nfb(s, p, _denc::build_indices_t{}); } template static typename std::enable_if::type bound_encode(const container& s, size_t& p, uint64_t f) { bound_encode_helper_fnb(s, p, _denc::build_indices_t{}); } template static typename std::enable_if::type bound_encode(const container& s, size_t& p, uint64_t f) { bound_encode_helper_fb(s, p, _denc::build_indices_t{}); } template static typename std::enable_if::type encode(const container& s, buffer::list::contiguous_appender& p) { encode_helper_nf(s, p, _denc::build_indices_t{}); } template static typename std::enable_if::type encode(const container& s, buffer::list::contiguous_appender& p, uint64_t f) { encode_helper_f(s, p, f, _denc::build_indices_t{}); } static void decode(container& s, buffer::ptr::iterator& p, uint64_t f = 0) { decode_helper(s, p, _denc::build_indices_t{}); } template static typename std::enable_if::type decode(container& s, buffer::list::iterator& p, uint64_t f = 0) { decode_helper(s, p, _denc::build_indices_t{}); } }; // // boost::optional // template struct denc_traits< boost::optional, typename std::enable_if::supported>::type> { using traits = denc_traits; static constexpr bool supported = true; static constexpr bool featured = traits::featured; static constexpr bool bounded = false; static constexpr bool need_contiguous = traits::need_contiguous; template static typename std::enable_if::type bound_encode(const boost::optional& v, size_t& p) { p += sizeof(bool); if (v) denc(*v, p); } template static typename std::enable_if::type bound_encode(const boost::optional& v, size_t& p, uint64_t f) { p += sizeof(bool); if (v) denc(*v, p); } template static typename std::enable_if::type encode(const boost::optional& v, bufferlist::contiguous_appender& p) { denc((bool)v, p); if (v) denc(*v, p); } template static typename std::enable_if::type encode(const boost::optional& v, bufferlist::contiguous_appender& p, uint64_t f) { denc((bool)v, p, f); if (v) denc(*v, p, f); } static void decode(boost::optional& v, buffer::ptr::iterator& p, uint64_t f = 0) { bool x; denc(x, p, f); if (x) { v = T{}; denc(*v, p, f); } else { v = boost::none; } } template static typename std::enable_if::type decode(boost::optional& v, buffer::list::iterator& p) { bool x; denc(x, p); if (x) { v = T{}; denc(*v, p); } else { v = boost::none; } } template static typename std::enable_if::type encode_nohead(const boost::optional& v, bufferlist::contiguous_appender& p) { if (v) denc(*v, p); } template static typename std::enable_if::type encode_nohead(const boost::optional& v, bufferlist::contiguous_appender& p, uint64_t f) { if (v) denc(*v, p, f); } static void decode_nohead(bool num, boost::optional& v, buffer::ptr::iterator& p, uint64_t f = 0) { if (num) { v = T(); denc(*v, p, f); } else { v = boost::none; } } }; template<> struct denc_traits { static constexpr bool supported = true; static constexpr bool featured = false; static constexpr bool bounded = true; static constexpr bool need_contiguous = false; static void bound_encode(const boost::none_t& v, size_t& p) { p += sizeof(bool); } static void encode(const boost::none_t& v, bufferlist::contiguous_appender& p) { denc(false, p); } }; // ---------------------------------------------------------------------- // class helpers // Write denc_traits<> for a class that defines bound_encode/encode/decode // methods. #define WRITE_CLASS_DENC(T) _DECLARE_CLASS_DENC(T, false) #define WRITE_CLASS_DENC_BOUNDED(T) _DECLARE_CLASS_DENC(T, true) #define _DECLARE_CLASS_DENC(T, b) \ template<> struct denc_traits { \ static constexpr bool supported = true; \ static constexpr bool featured = false; \ static constexpr bool bounded = b; \ static constexpr bool need_contiguous = !_denc::has_legacy_denc::value;\ static void bound_encode(const T& v, size_t& p, uint64_t f=0) { \ v.bound_encode(p); \ } \ static void encode(const T& v, buffer::list::contiguous_appender& p, \ uint64_t f=0) { \ v.encode(p); \ } \ static void decode(T& v, buffer::ptr::iterator& p, uint64_t f=0) { \ v.decode(p); \ } \ }; #define WRITE_CLASS_DENC_FEATURED(T) _DECLARE_CLASS_DENC_FEATURED(T, false) #define WRITE_CLASS_DENC_FEATURED_BOUNDED(T) _DECLARE_CLASS_DENC_FEATURED(T, true) #define _DECLARE_CLASS_DENC_FEATURED(T, b) \ template<> struct denc_traits { \ static constexpr bool supported = true; \ static constexpr bool featured = true; \ static constexpr bool bounded = b; \ static constexpr bool need_contiguous = !_denc::has_legacy_denc::value;\ static void bound_encode(const T& v, size_t& p, uint64_t f) { \ v.bound_encode(p, f); \ } \ static void encode(const T& v, buffer::list::contiguous_appender& p, \ uint64_t f) { \ v.encode(p, f); \ } \ static void decode(T& v, buffer::ptr::iterator& p, uint64_t f=0) { \ v.decode(p, f); \ } \ }; // ---------------------------------------------------------------------- // encode/decode wrappers // These glue the new-style denc world into old-style calls to encode // and decode by calling into denc_traits<> methods (when present). template> inline typename std::enable_if::type encode( const T& o, bufferlist& bl, uint64_t features_unused=0) { size_t len = 0; traits::bound_encode(o, len); auto a = bl.get_contiguous_appender(len); traits::encode(o, a); } template> inline typename std::enable_if::type encode( const T& o, bufferlist& bl, uint64_t features) { size_t len = 0; traits::bound_encode(o, len, features); auto a = bl.get_contiguous_appender(len); traits::encode(o, a, features); } template> inline typename std::enable_if::type decode( T& o, bufferlist::iterator& p) { if (p.end()) throw buffer::end_of_buffer(); const auto& bl = p.get_bl(); const auto remaining = bl.length() - p.get_off(); // it is expensive to rebuild a contigous buffer and drop it, so avoid this. if (p.get_current_ptr().get_raw() != bl.back().get_raw() && remaining > CEPH_PAGE_SIZE) { traits::decode(o, p); } else { // ensure we get a contigous buffer... until the end of the // bufferlist. we don't really know how much we'll need here, // unfortunately. hopefully it is already contiguous and we're just // bumping the raw ref and initializing the ptr tmp fields. bufferptr tmp; bufferlist::iterator t = p; t.copy_shallow(remaining, tmp); auto cp = tmp.begin(); traits::decode(o, cp); p.advance((ssize_t)cp.get_offset()); } } template> inline typename std::enable_if::type decode( T& o, bufferlist::iterator& p) { if (p.end()) throw buffer::end_of_buffer(); // ensure we get a contigous buffer... until the end of the // bufferlist. we don't really know how much we'll need here, // unfortunately. hopefully it is already contiguous and we're just // bumping the raw ref and initializing the ptr tmp fields. bufferptr tmp; bufferlist::iterator t = p; t.copy_shallow(p.get_bl().length() - p.get_off(), tmp); auto cp = tmp.begin(); traits::decode(o, cp); p.advance((ssize_t)cp.get_offset()); } // nohead variants template> inline typename std::enable_if::type encode_nohead( const T& o, bufferlist& bl) { size_t len = 0; traits::bound_encode(o, len); auto a = bl.get_contiguous_appender(len); traits::encode_nohead(o, a); } template> inline typename std::enable_if::type decode_nohead( size_t num, T& o, bufferlist::iterator& p) { if (!num) return; if (p.end()) throw buffer::end_of_buffer(); bufferptr tmp; bufferlist::iterator t = p; t.copy_shallow(p.get_bl().length() - p.get_off(), tmp); auto cp = tmp.begin(); traits::decode_nohead(num, o, cp); p.advance((ssize_t)cp.get_offset()); } // ---------------------------------------------------------------- // DENC // These are some class methods we need to do the version and length // wrappers for DENC_{START,FINISH} for inter-version // interoperability. #define DENC_HELPERS \ /* bound_encode */ \ static void _denc_start(size_t& p, \ __u8 *struct_v, \ __u8 *struct_compat, \ char **, uint32_t *) { \ p += 2 + 4; \ } \ static void _denc_finish(size_t& p, \ __u8 *struct_v, \ __u8 *struct_compat, \ char **, uint32_t *) { } \ /* encode */ \ static void _denc_start(bufferlist::contiguous_appender& p, \ __u8 *struct_v, \ __u8 *struct_compat, \ char **len_pos, \ uint32_t *start_oob_off) { \ denc(*struct_v, p); \ denc(*struct_compat, p); \ *len_pos = p.get_pos_add(4); \ *start_oob_off = p.get_out_of_band_offset(); \ } \ static void _denc_finish(bufferlist::contiguous_appender& p, \ __u8 *struct_v, \ __u8 *struct_compat, \ char **len_pos, \ uint32_t *start_oob_off) { \ *(__le32*)*len_pos = p.get_pos() - *len_pos - sizeof(uint32_t) + \ p.get_out_of_band_offset() - *start_oob_off; \ } \ /* decode */ \ static void _denc_start(buffer::ptr::iterator& p, \ __u8 *struct_v, \ __u8 *struct_compat, \ char **start_pos, \ uint32_t *struct_len) { \ denc(*struct_v, p); \ denc(*struct_compat, p); \ denc(*struct_len, p); \ *start_pos = const_cast(p.get_pos()); \ } \ static void _denc_finish(buffer::ptr::iterator& p, \ __u8 *struct_v, __u8 *struct_compat, \ char **start_pos, \ uint32_t *struct_len) { \ const char *pos = p.get_pos(); \ char *end = *start_pos + *struct_len; \ assert(pos <= end); \ if (pos < end) { \ p.advance(end - pos); \ } \ } // Helpers for versioning the encoding. These correspond to the // {ENCODE,DECODE}_{START,FINISH} macros. #define DENC_START(v, compat, p) \ __u8 struct_v = v; \ __u8 struct_compat = compat; \ char *_denc_pchar; \ uint32_t _denc_u32; \ _denc_start(p, &struct_v, &struct_compat, &_denc_pchar, &_denc_u32); \ do { #define DENC_FINISH(p) \ } while (false); \ _denc_finish(p, &struct_v, &struct_compat, &_denc_pchar, &_denc_u32); // ---------------------------------------------------------------------- // Helpers for writing a unified bound_encode/encode/decode // implementation that won't screw up buffer size estimations. #define DENC(Type, v, p) \ DENC_HELPERS \ void bound_encode(size_t& p) const { \ _denc_friend(*this, p); \ } \ void encode(bufferlist::contiguous_appender& p) const { \ DENC_DUMP_PRE(Type); \ _denc_friend(*this, p); \ DENC_DUMP_POST(Type); \ } \ void decode(buffer::ptr::iterator& p) { \ _denc_friend(*this, p); \ } \ template \ friend typename std::enable_if::value || \ boost::is_same::value>::type \ _denc_friend(T& v, P& p) #define DENC_FEATURED(Type, v, p, f) \ DENC_HELPERS \ void bound_encode(size_t& p, uint64_t f) const { \ _denc_friend(*this, p, f); \ } \ void encode(bufferlist::contiguous_appender& p, uint64_t f) const { \ DENC_DUMP_PRE(Type); \ _denc_friend(*this, p, f); \ DENC_DUMP_POST(Type); \ } \ void decode(buffer::ptr::iterator& p, uint64_t f=0) { \ _denc_friend(*this, p, f); \ } \ template \ friend typename std::enable_if::value || \ boost::is_same::value>::type \ _denc_friend(T& v, P& p, uint64_t f) #endif