From: Sage Weil Date: Tue, 13 Sep 2016 18:04:22 +0000 (-0400) Subject: include/denc: new-style encoding framework X-Git-Tag: v11.1.0~617^2~18 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=18d754f62032b4892b648592cb1b98df1b34e1f1;p=ceph.git include/denc: new-style encoding framework #include it from encoding.h so that we can make old STL container helpers conditional on a new-style helper not being available. Signed-off-by: Sage Weil --- diff --git a/src/include/buffer.h b/src/include/buffer.h index 57f29944d652..48df6cffb70f 100644 --- a/src/include/buffer.h +++ b/src/include/buffer.h @@ -186,7 +186,7 @@ namespace buffer CEPH_BUFFER_API { friend class ptr; public: - const char *c_str_add(size_t n) { + const char *get_pos_add(size_t n) { const char *r = pos; pos += n; if (pos > end_ptr) @@ -196,7 +196,7 @@ namespace buffer CEPH_BUFFER_API { ptr get_ptr(size_t len) { if (deep) { - return buffer::copy(c_str_add(len), len); + return buffer::copy(get_pos_add(len), len); } else { size_t off = pos - bp->c_str(); pos += len; @@ -207,7 +207,7 @@ namespace buffer CEPH_BUFFER_API { } ptr get_preceding_ptr(size_t len) { if (deep) { - return buffer::copy(c_str() - len, len); + return buffer::copy(get_pos() - len, len); } else { size_t off = pos - bp->c_str(); return ptr(*bp, off - len, len); @@ -220,10 +220,10 @@ namespace buffer CEPH_BUFFER_API { throw end_of_buffer(); } - const char *c_str() { + const char *get_pos() { return pos; } - const char *end_c_str() { + const char *get_end() { return end_ptr; } @@ -527,12 +527,12 @@ namespace buffer CEPH_BUFFER_API { maybe_inline_memcpy(pos, p, l, 16); pos += l; } - char *get_ptr_add(size_t len) { + char *get_pos_add(size_t len) { char *r = pos; pos += len; return r; } - char *get_ptr() { + char *get_pos() { return pos; } diff --git a/src/include/denc.h b/src/include/denc.h new file mode 100644 index 000000000000..2de284c8a4e4 --- /dev/null +++ b/src/include/denc.h @@ -0,0 +1,1206 @@ +// -*- 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/int_types.h" +#include "include/intarith.h" +#include "include/memory.h" +#include "byteorder.h" +#include "buffer.h" + +template +struct denc_traits { + enum { supported = 0 }; + enum { featured = false }; + enum { bounded = false }; +}; + + +// hack for debug only; FIXME +//#include +//using std::cout; + + + +/* + + 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); + + 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); + + - 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. The feature argument for decode is + ignored. + + - 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 { + enum { supported = true }; + enum { bounded = false }; + enum { featured = false }; + 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); + }; + + or (for featured objects) + + template<> + struct denc_traits { + enum { supported = true }; + enum { bounded = false }; + enum { featured = 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); + }; + + - 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 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 + +#define WRITE_RAW_DENC(type) \ + template<> \ + struct denc_traits { \ + enum { supported = 2 }; \ + enum { featured = false }; \ + enum { bounded = true }; \ + static void bound_encode(const type &o, size_t& p, uint64_t f=0) { \ + p += sizeof(type); \ + } \ + static void encode(const type &o, \ + buffer::list::contiguous_appender& p, \ + uint64_t f=0) { \ + p.append((const char*)&o, sizeof(o)); \ + } \ + static void decode(type& o, buffer::ptr::iterator &p) { \ + o = *(type *)p.get_pos_add(sizeof(o)); \ + } \ + }; + +WRITE_RAW_DENC(ceph_le64) +WRITE_RAW_DENC(ceph_le32) +WRITE_RAW_DENC(ceph_le16) +WRITE_RAW_DENC(uint8_t); +#ifndef _CHAR_IS_SIGNED +WRITE_RAW_DENC(int8_t); +#endif + + + +// ----------------------------------------------------------------------- +// integer types + +// itype == internal type +// otype == external type, i.e., the type on the wire + +// NOTE: set supported == 2 instead of true. This prevents these from +// getting glued into the legacy encode/decode methods; the overhead +// of setting up a contiguous_appender etc is likely to be slower. + +#define WRITE_INT_DENC(itype, etype) \ + template<> \ + struct denc_traits { \ + enum { supported = 2 }; \ + enum { featured = false }; \ + enum { bounded = true }; \ + static void bound_encode(const itype &o, size_t& p, uint64_t f=0) { \ + p += sizeof(etype); \ + } \ + static void encode(const itype &o, buffer::list::contiguous_appender& p, \ + uint64_t f=0) { \ + *(etype *)p.get_pos_add(sizeof(etype)) = o; \ + } \ + static void decode(itype& o, buffer::ptr::iterator &p) { \ + o = *(etype*)p.get_pos_add(sizeof(etype)); \ + } \ + }; + +WRITE_INT_DENC(uint16_t, __le16); +WRITE_INT_DENC(int16_t, __le16); +WRITE_INT_DENC(uint32_t, __le32); +WRITE_INT_DENC(int32_t, __le32); +WRITE_INT_DENC(uint64_t, __le64); +WRITE_INT_DENC(int64_t, __le64); + + +// 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; + 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; + 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; + } + int lowznib = v ? (ctz(v) / 4) : 0; + 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; + 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); +} + + +// --------------------------------------------------------------------- +// base types and containers + +// +// std::string +// +template<> +struct denc_traits { + enum { supported = true }; + enum { featured = false }; + enum { bounded = false }; + static void bound_encode(const std::string& s, size_t& p, uint64_t f=0) { + p += sizeof(uint32_t) + s.size(); + } + static void encode(const std::string& 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(std::string& s, buffer::ptr::iterator& p, uint64_t f=0) { + uint32_t len; + ::denc(len, p); + s.clear(); + if (len) { + s.append(p.get_pos_add(len), len); + } + } +}; + +// +// bufferptr +// +template<> +struct denc_traits { + enum { supported = 2 }; + enum { featured = false }; + enum { bounded = 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); + } +}; + +// +// bufferlist +// +template<> +struct denc_traits { + enum { supported = 2 }; + enum { featured = false }; + enum { bounded = 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)); + } +}; + +// +// 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; + + enum { supported = true }; + enum { featured = a_traits::featured || b_traits::featured }; + enum { bounded = a_traits::bounded && b_traits::bounded }; + + 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) { + denc(v.first, p); + denc(v.second, p); + } +}; + +// +// std::list +// +template +struct denc_traits< + std::list, + typename std::enable_if::supported>::type> { + typedef denc_traits traits; + + enum { supported = true }; + enum { featured = traits::featured }; + enum { bounded = false }; + + template + static typename std::enable_if::type + bound_encode(const std::list& 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 std::list& s, size_t& p) { + size_t elem_size = 0; + denc(*(const T*)nullptr, elem_size); + p += sizeof(uint32_t) + elem_size * s.size(); + } + template + static typename std::enable_if::type + bound_encode(const std::list& 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 std::list& s, size_t& p, uint64_t f) { + size_t elem_size = 0; + denc(*(const T*)nullptr, elem_size, f); + p += sizeof(uint32_t) + elem_size * s.size(); + } + + template + static typename std::enable_if::type + encode(const std::list& s, buffer::list::contiguous_appender& p) { + denc((uint32_t)s.size(), p); + for (const T& e : s) { + denc(e, p); + } + } + template + static typename std::enable_if::type + encode(const std::list& s, buffer::list::contiguous_appender& p, + uint64_t f) { + denc((uint32_t)s.size(), p); + for (const T& e : s) { + denc(e, p, f); + } + } + static void decode(std::list& s, buffer::ptr::iterator& p) { + s.clear(); + uint32_t num; + denc(num, p); + while (num--) { + s.emplace_back(T()); + denc(s.back(), p); + } + } +}; + +// +// std::vector +// +template +struct denc_traits< + std::vector, + typename std::enable_if::supported>::type> { + typedef denc_traits traits; + + enum { supported = true }; + enum { featured = traits::featured }; + enum { bounded = false }; + + template + static typename std::enable_if::type + bound_encode(const std::vector& 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 std::vector& s, size_t& p) { + size_t elem_size = 0; + denc(*(const T*)nullptr, elem_size); + p += sizeof(uint32_t) + elem_size * s.size(); + } + template + static typename std::enable_if::type + bound_encode(const std::vector& 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 std::vector& s, size_t& p, uint64_t f) { + size_t elem_size = 0; + denc(*(const T*)nullptr, elem_size, f); + p += sizeof(uint32_t) + elem_size * s.size(); + } + + template + static typename std::enable_if::type + encode(const std::vector& s, buffer::list::contiguous_appender& p) { + denc((uint32_t)s.size(), p); + for (const T& e : s) { + denc(e, p); + } + } + template + static typename std::enable_if::type + encode(const std::vector& s, buffer::list::contiguous_appender& p, + uint64_t f) { + denc((uint32_t)s.size(), p); + for (const T& e : s) { + denc(e, p, f); + } + } + static void decode(std::vector& s, buffer::ptr::iterator& p) { + s.clear(); + uint32_t num; + denc(num, p); + s.resize(num); + for (unsigned i=0; i +// +template +struct denc_traits< + std::set, + typename std::enable_if::supported>::type> { + typedef denc_traits traits; + + enum { supported = true }; + enum { featured = traits::featured }; + enum { bounded = false }; + + template + static typename std::enable_if::type + bound_encode(const std::set& 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 std::set& s, size_t& p) { + size_t elem_size = 0; + denc(*(const T*)nullptr, elem_size); + p += sizeof(uint32_t) + elem_size * s.size(); + } + template + static typename std::enable_if::type + bound_encode(const std::set& 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 std::set& s, size_t& p, uint64_t f) { + size_t elem_size = 0; + denc(*(const T*)nullptr, elem_size, f); + p += sizeof(uint32_t) + elem_size * s.size(); + } + + template + static typename std::enable_if::type + encode(const std::set& s, buffer::list::contiguous_appender& p) { + denc((uint32_t)s.size(), p); + for (const T& e : s) { + denc(e, p); + } + } + template + static typename std::enable_if::type + encode(const std::set& s, buffer::list::contiguous_appender& p, + uint64_t f) { + denc((uint32_t)s.size(), p); + for (const T& e : s) { + denc(e, p, f); + } + } + static void decode(std::set& s, buffer::ptr::iterator& p) { + s.clear(); + uint32_t num; + denc(num, p); + while (num--) { + T temp; + denc(temp, p); + s.insert(temp); + } + } +}; + +// +// std::map +// +template +struct denc_traits< + std::map, + typename std::enable_if::supported && + denc_traits::supported>::type> { + typedef denc_traits a_traits; + typedef denc_traits b_traits; + + enum { supported = true }; + enum { featured = a_traits::featured || b_traits::featured }; + enum { bounded = a_traits::bounded && b_traits::bounded }; + + template + static typename std::enable_if::type + bound_encode(const std::map& v, size_t& p) { + denc((uint32_t)v.size(), p); + for (const auto& i : v) { + denc(i.first, p); + denc(i.second, p); + } + } + template + static typename std::enable_if::type + bound_encode(const std::map& v, size_t& p, uint64_t f) { + denc((uint32_t)v.size(), p); + for (const auto& i : v) { + denc(i.first, p, f); + denc(i.second, p, f); + } + } + template + static typename std::enable_if::type + bound_encode(const std::map& v, size_t& p) { + denc((uint32_t)v.size(), p); + size_t elem_size = 0; + denc(*(A*)nullptr, elem_size); + denc(*(B*)nullptr, elem_size); + p += v.size() * elem_size; + } + template + static typename std::enable_if::type + bound_encode(const std::map& v, size_t& p, uint64_t f) { + denc((uint32_t)v.size(), p); + size_t elem_size = 0; + denc(*(A*)nullptr, elem_size, f); + denc(*(B*)nullptr, elem_size, f); + p += v.size() * elem_size; + } + + template + static typename std::enable_if::type + encode(const std::map& v, bufferlist::contiguous_appender& p) { + denc((uint32_t)v.size(), p); + for (const auto& i : v) { + denc(i.first, p); + denc(i.second, p); + } + } + template + static typename std::enable_if::type + encode(const std::map& v, bufferlist::contiguous_appender& p, + uint64_t f) { + denc((uint32_t)v.size(), p); + for (const auto& i : v) { + denc(i.first, p, f); + denc(i.second, p, f); + } + } + + static void decode(std::map& v, buffer::ptr::iterator& p) { + v.clear(); + uint32_t num; + denc(num, p); + A key; + while (num--) { + denc(key, p); + denc(v[key], 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 { \ + enum { supported = true }; \ + enum { featured = false }; \ + enum { bounded = b }; \ + 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) { \ + 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 { \ + enum { supported = true }; \ + enum { featured = true }; \ + enum { bounded = b }; \ + 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) { \ + v.decode(p); \ + } \ + }; + + +// ---------------------------------------------------------------------- +// 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(); + // 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()); +} + +template> +inline typename std::enable_if::type decode( + T& o, + bufferlist::iterator& p) +{ + 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(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_friend(*this, p); \ + } \ + 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_friend(*this, p, f); \ + } \ + void decode(buffer::ptr::iterator& p) { \ + _denc_friend(*this, p, 0); \ + } \ + template \ + friend typename std::enable_if::value || \ + boost::is_same::value>::type \ + _denc_friend(T& v, P& p, uint64_t f) + +#endif diff --git a/src/include/encoding.h b/src/include/encoding.h index 2825144c1fa2..89fa52199620 100644 --- a/src/include/encoding.h +++ b/src/include/encoding.h @@ -20,6 +20,10 @@ #include "byteorder.h" #include "buffer.h" + +// pull in the new-style encoding so that we get the denc_traits<> definition. +#include "denc.h" + #include "assert.h" using namespace ceph; @@ -349,37 +353,48 @@ inline void decode(boost::tuple &t, bufferlist::iterator &bp) decode(boost::get<2>(t), bp); } -// pair -template -inline void encode(const std::pair &p, bufferlist &bl, uint64_t features) +// std::pair +template, typename b_traits=denc_traits> +inline typename std::enable_if::type + encode(const std::pair &p, bufferlist &bl, uint64_t features) { encode(p.first, bl, features); encode(p.second, bl, features); } -template -inline void encode(const std::pair &p, bufferlist &bl) +template, typename b_traits=denc_traits> +inline typename std::enable_if::type + encode(const std::pair &p, bufferlist &bl) { encode(p.first, bl); encode(p.second, bl); } -template -inline void decode(std::pair &pa, bufferlist::iterator &p) +template, typename b_traits=denc_traits> +inline typename std::enable_if::type + decode(std::pair &pa, bufferlist::iterator &p) { decode(pa.first, p); decode(pa.second, p); } -// list -template -inline void encode(const std::list& ls, bufferlist& bl) +// std::list +template> +inline typename std::enable_if::type + encode(const std::list& ls, bufferlist& bl) { __u32 n = (__u32)(ls.size()); // c++11 std::list::size() is O(1) encode(n, bl); for (typename std::list::const_iterator p = ls.begin(); p != ls.end(); ++p) encode(*p, bl); } -template -inline void encode(const std::list& ls, bufferlist& bl, uint64_t features) +template> +inline typename std::enable_if::type + encode(const std::list& ls, bufferlist& bl, uint64_t features) { // should i pre- or post- count? if (!ls.empty()) { @@ -400,8 +415,9 @@ inline void encode(const std::list& ls, bufferlist& bl, uint64_t features) encode(*p, bl, features); } } -template -inline void decode(std::list& ls, bufferlist::iterator& p) +template> +inline typename std::enable_if::type + decode(std::list& ls, bufferlist::iterator& p) { __u32 n; decode(n, p); @@ -413,6 +429,7 @@ inline void decode(std::list& ls, bufferlist::iterator& p) } } +// std::list> template inline void encode(const std::list >& ls, bufferlist& bl) { @@ -442,17 +459,19 @@ inline void decode(std::list >& ls, bufferlist::iterator& p) } } -// set -template -inline void encode(const std::set& s, bufferlist& bl) +// std::set +template> +inline typename std::enable_if::type + encode(const std::set& s, bufferlist& bl) { __u32 n = (__u32)(s.size()); encode(n, bl); for (typename std::set::const_iterator p = s.begin(); p != s.end(); ++p) encode(*p, bl); } -template -inline void decode(std::set& s, bufferlist::iterator& p) +template> +inline typename std::enable_if::type + decode(std::set& s, bufferlist::iterator& p) { __u32 n; decode(n, p); @@ -464,16 +483,18 @@ inline void decode(std::set& s, bufferlist::iterator& p) } } -template -inline void encode(const std::set& s, bufferlist& bl) +template> +inline typename std::enable_if::type + encode(const std::set& s, bufferlist& bl) { __u32 n = (__u32)(s.size()); encode(n, bl); for (typename std::set::const_iterator p = s.begin(); p != s.end(); ++p) encode(*p, bl); } -template -inline void decode(std::set& s, bufferlist::iterator& p) +template> +inline typename std::enable_if::type + decode(std::set& s, bufferlist::iterator& p) { __u32 n; decode(n, p); @@ -542,25 +563,28 @@ inline void decode(std::vector& v, bufferlist::iterator& p) v[i] = new T(p); } */ -// vector -template -inline void encode(const std::vector& v, bufferlist& bl, uint64_t features) +// std::vector +template> +inline typename std::enable_if::type + encode(const std::vector& v, bufferlist& bl, uint64_t features) { __u32 n = (__u32)(v.size()); encode(n, bl); for (typename std::vector::const_iterator p = v.begin(); p != v.end(); ++p) encode(*p, bl, features); } -template -inline void encode(const std::vector& v, bufferlist& bl) +template> +inline typename std::enable_if::type + encode(const std::vector& v, bufferlist& bl) { __u32 n = (__u32)(v.size()); encode(n, bl); for (typename std::vector::const_iterator p = v.begin(); p != v.end(); ++p) encode(*p, bl); } -template -inline void decode(std::vector& v, bufferlist::iterator& p) +template> +inline typename std::enable_if::type + decode(std::vector& v, bufferlist::iterator& p) { __u32 n; decode(n, p); @@ -645,8 +669,11 @@ inline void decode(std::map& m, bufferlist::iterator& p) }*/ // map -template -inline void encode(const std::map& m, bufferlist& bl) +template, typename u_traits=denc_traits> +inline typename std::enable_if::type + encode(const std::map& m, bufferlist& bl) { __u32 n = (__u32)(m.size()); encode(n, bl); @@ -665,8 +692,11 @@ inline void encode(const std::map& m, bufferlist& bl) encode(p->second, bl); } } -template -inline void encode(const std::map& m, bufferlist& bl, uint64_t features) +template, typename u_traits=denc_traits> +inline typename std::enable_if::type + encode(const std::map& m, bufferlist& bl, uint64_t features) { __u32 n = (__u32)(m.size()); encode(n, bl); @@ -675,8 +705,11 @@ inline void encode(const std::map& m, bufferlist& bl, uint64_t features) encode(p->second, bl, features); } } -template -inline void decode(std::map& m, bufferlist::iterator& p) +template, typename u_traits=denc_traits> +inline typename std::enable_if::type + decode(std::map& m, bufferlist::iterator& p) { __u32 n; decode(n, p); diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt index 9dd2e18e66a1..d80c03785171 100644 --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -698,6 +698,13 @@ add_executable(unittest_arch add_ceph_unittest(unittest_arch ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/unittest_arch) target_link_libraries(unittest_arch global) +# unittest_denc +add_executable(unittest_denc + test_denc.cc + ) +add_ceph_unittest(unittest_denc ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/unittest_denc) +target_link_libraries(unittest_denc os global) + # unittest_crypto add_executable(unittest_crypto crypto.cc diff --git a/src/test/test_denc.cc b/src/test/test_denc.cc new file mode 100644 index 000000000000..bc550de554f9 --- /dev/null +++ b/src/test/test_denc.cc @@ -0,0 +1,483 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab +/* + * Ceph distributed storage system + * + * Copyright (C) 2016 Red Hat + * + * Author: Sage Weil + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + */ + +#include + +#include "global/global_init.h" +#include "common/ceph_argparse.h" +#include "global/global_context.h" +#include "gtest/gtest.h" + +#include "include/denc.h" + +// test helpers + +template +void test_encode_decode(T v) { + bufferlist bl; + ::encode(v, bl); + bufferlist::iterator p = bl.begin(); + T out; + ::decode(out, p); + ASSERT_EQ(v, out); +} + +template +void test_denc(T v) { + // estimate + size_t s = 0; + denc(v, s); + ASSERT_NE(s, 0u); + + // encode + bufferlist bl; + { + auto a = bl.get_contiguous_appender(sizeof(T) * 3); + denc(v, a); + } + ASSERT_LE(bl.length(), s); + + // decode + bl.rebuild(); + T out; + auto bpi = bl.front().begin(); + denc(out, bpi); + ASSERT_EQ(v, out); + ASSERT_EQ(bpi.get_pos(), bl.c_str() + bl.length()); + + // test glue + test_encode_decode(v); +} + +template +void test_encode_decode_featured(T v) { + bufferlist bl; + ::encode(v, bl, 123); + bufferlist::iterator p = bl.begin(); + T out; + ::decode(out, p); + ASSERT_EQ(v, out); +} + +template +void test_denc_featured(T v) { + // estimate + size_t s = 0; + denc(v, s, 0); + ASSERT_GT(s, 0u); + + // encode + bufferlist bl; + { + auto a = bl.get_contiguous_appender(sizeof(T) * 3); + denc(v, a, 1); + } + ASSERT_LE(bl.length(), s); + + // decode + bl.rebuild(); + T out; + auto bpi = bl.front().begin(); + denc(out, bpi, 1); + ASSERT_EQ(v, out); + ASSERT_EQ(bpi.get_pos(), bl.c_str() + bl.length()); + + // test glue + test_encode_decode_featured(v); +} + + +// hooks to count bound calls + +struct counts_t { + int num_bound_encode = 0; + int num_encode = 0; + int num_decode = 0; + void reset() { + num_bound_encode = 0; + num_encode = 0; + num_decode = 0; + } +} counts; + +struct denc_counter_t { + void bound_encode(size_t& p) const { + ++counts.num_bound_encode; + ++p; // denc.h does not like 0-length objects + } + void encode(buffer::list::contiguous_appender& p) const { + p.append("a", 1); + ++counts.num_encode; + } + void decode(buffer::ptr::iterator &p) { + p.advance(1); + ++counts.num_decode; + } +}; +WRITE_CLASS_DENC(denc_counter_t) + +struct denc_counter_bounded_t { + void bound_encode(size_t& p) const { + ++counts.num_bound_encode; + ++p; // denc.h does not like 0-length objects + } + void encode(buffer::list::contiguous_appender& p) const { + p.append("a", 1); + ++counts.num_encode; + } + void decode(buffer::ptr::iterator &p) { + p.advance(1); + ++counts.num_decode; + } +}; +WRITE_CLASS_DENC_BOUNDED(denc_counter_bounded_t) + +TEST(denc, denc_counter) +{ + denc_counter_t single, single2; + { + bufferlist bl; + ::encode(single, bl); + ::decode(single2, bl); + } + ASSERT_EQ(counts.num_bound_encode, 1); + ASSERT_EQ(counts.num_encode, 1); + ASSERT_EQ(counts.num_decode, 1); + counts.reset(); +} + +TEST(denc, simple) +{ + test_denc((uint8_t)4); + test_denc((int8_t)-5); + test_denc((uint16_t)6); + test_denc((int16_t)-7); + test_denc((uint32_t)8); + test_denc((int32_t)-9); + test_denc((uint64_t)10); + test_denc((int64_t)-11); +} + +TEST(denc, string) +{ + string a, b("hi"), c("multi\nline\n"); + test_denc(a); + test_denc(b); + test_denc(c); +} + +struct legacy_t { + int32_t a = 1; + void encode(bufferlist& bl) const { + ::encode(a, bl); + } + void decode(bufferlist::iterator& p) { + ::decode(a, p); + } + legacy_t() {} + legacy_t(int32_t i) : a(i) {} + friend bool operator<(const legacy_t& l, const legacy_t& r) { + return l.a < r.a; + } + friend bool operator==(const legacy_t& l, const legacy_t& r) { + return l.a == r.a; + } +}; +WRITE_CLASS_ENCODER(legacy_t) + +TEST(denc, vector) +{ + { + cout << "vector" << std::endl; + std::vector s; + s.push_back("foo"); + s.push_back("bar"); + s.push_back("baz"); + counts.reset(); + test_denc(s); + } + { + cout << "vector" << std::endl; + std::vector s; + s.push_back(1); + s.push_back(2); + s.push_back(3); + test_denc(s); + } + { + cout << "vector" << std::endl; + std::vector s; + s.push_back(legacy_t(1)); + s.push_back(legacy_t(2)); + test_encode_decode(s); + } + { + counts.reset(); + vector v, v2; + v.resize(100); + { + bufferlist bl; + ::encode(v, bl); + ::decode(v2, bl); + } + ASSERT_EQ(counts.num_bound_encode, 100); + ASSERT_EQ(counts.num_encode, 100); + ASSERT_EQ(counts.num_decode, 100); + } + { + counts.reset(); + vector v, v2; + v.resize(100); + { + bufferlist bl; + ::encode(v, bl); + ::decode(v2, bl); + } + ASSERT_EQ(counts.num_bound_encode, 1); + ASSERT_EQ(counts.num_encode, 100); + ASSERT_EQ(counts.num_decode, 100); + } +} + +TEST(denc, list) +{ + { + cout << "list" << std::endl; + std::list s; + s.push_back("foo"); + s.push_back("bar"); + s.push_back("baz"); + test_denc(s); + } + { + cout << "list" << std::endl; + std::list s; + s.push_back(1); + s.push_back(2); + s.push_back(3); + test_denc(s); + } + { + cout << "list" << std::endl; + std::list s; + s.push_back(legacy_t(1)); + s.push_back(legacy_t(2)); + test_encode_decode(s); + } + { + counts.reset(); + list l, l2; + for (unsigned i=0; i<100; ++i) { + l.emplace_back(denc_counter_bounded_t()); + } + { + bufferlist bl; + ::encode(l, bl); + ::decode(l2, bl); + } + ASSERT_EQ(counts.num_bound_encode, 1); + ASSERT_EQ(counts.num_encode, 100); + ASSERT_EQ(counts.num_decode, 100); + } +} + +TEST(denc, set) +{ + { + cout << "set" << std::endl; + std::set s; + s.insert("foo"); + s.insert("bar"); + s.insert("baz"); + test_denc(s); + } + { + cout << "set" << std::endl; + std::set s; + s.insert(1); + s.insert(2); + s.insert(3); + test_denc(s); + } + { + cout << "set" << std::endl; + std::set s; + s.insert(legacy_t(1)); + s.insert(legacy_t(2)); + test_encode_decode(s); + } +} + +struct foo_t { + int32_t a = 0; + uint64_t b = 123; + + DENC(foo_t, v, p) { + DENC_START(1, 1, p); + ::denc(v.a, p); + ::denc(v.b, p); + DENC_FINISH(p); + } + + friend bool operator==(const foo_t& l, const foo_t& r) { + return l.a == r.a && l.b == r.b; + } +}; +WRITE_CLASS_DENC_BOUNDED(foo_t) + +struct foo2_t { + int32_t c = 0; + uint64_t d = 123; + + DENC(foo2_t, v, p) { + DENC_START(1, 1, p); + ::denc(v.c, p); + ::denc(v.d, p); + DENC_FINISH(p); + } + + friend bool operator==(const foo2_t& l, const foo2_t& r) { + return l.c == r.c && l.d == r.d; + } +}; +WRITE_CLASS_DENC_BOUNDED(foo2_t) + + +struct bar_t { + int32_t a = 0; + uint64_t b = 123; + + DENC_FEATURED(bar_t, v, p, f) { + ::denc(v.a, p, f); + ::denc(v.b, p, f); + } + + friend bool operator==(const bar_t& l, const bar_t& r) { + return l.a == r.a && l.b == r.b; + } +}; +WRITE_CLASS_DENC_FEATURED_BOUNDED(bar_t) + +TEST(denc, foo) +{ + foo_t a; + test_denc(a); + bufferlist bl; + ::encode(a, bl); + bl.hexdump(cout); +} + +TEST(denc, bar) +{ + bar_t a; + test_denc_featured(a); +} + + + +TEST(denc, pair) +{ + pair p; + bufferlist bl; + { + auto a = bl.get_contiguous_appender(1000); + denc(p, a); + ::encode(p, bl); + } + + pair lp; + ::encode(lp, bl); +} + +TEST(denc, map) +{ + { + cout << "map" << std::endl; + std::map s; + s["foo"] = foo_t(); + s["bar"] = foo_t(); + s["baz"] = foo_t(); + test_denc(s); + } + { + cout << "map" << std::endl; + std::map s; + s["foo"] = bar_t(); + s["bar"] = bar_t(); + s["baz"] = bar_t(); + test_denc_featured(s); + } + { + cout << "map" << std::endl; + std::map s; + s["foo"] = legacy_t(1); + s["bar"] = legacy_t(2); + test_encode_decode(s); + } +} + + + +TEST(denc, bufferptr_shallow_and_deep) { + // shallow encode + int32_t i = 1; + bufferptr p1("foo", 3); + bufferlist bl; + { + auto a = bl.get_contiguous_appender(100); + denc(i, a); + denc(p1, a); + denc(i, a); + } + cout << "bl is " << bl << std::endl; + bl.hexdump(cout); + ASSERT_EQ(3u, bl.get_num_buffers()); + + bufferlist bl2 = bl; + bl.rebuild(); + bl2.rebuild(); + + // shallow decode + { + cout << "bl is " << bl << std::endl; + bl.hexdump(cout); + auto p = bl.front().begin(); + bufferptr op; + int32_t i; + denc(i, p); + denc(op, p); + denc(i, p); + ASSERT_EQ(3u, op.length()); + ASSERT_EQ('f', op[0]); + memset(bl.c_str(), 0, bl.length()); + ASSERT_EQ(0, op[0]); + } + + // deep decode + { + cout << "bl is " << bl2 << std::endl; + bl2.hexdump(cout); + auto p = bl2.front().begin_deep(); + bufferptr op; + int32_t i; + denc(i, p); + denc(op, p); + denc(i, p); + ASSERT_EQ('f', op[0]); + memset(bl2.c_str(), 1, bl2.length()); + ASSERT_EQ('f', op[0]); + } +}