From 19fcec84d8d7d21e796c7624e521b60d28ee21ed Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 20:45:59 +0200 Subject: Adding upstream version 16.2.11+ds. Signed-off-by: Daniel Baumann --- src/include/denc.h | 1903 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1903 insertions(+) create mode 100644 src/include/denc.h (limited to 'src/include/denc.h') diff --git a/src/include/denc.h b/src/include/denc.h new file mode 100644 index 000000000..10fa8d0c8 --- /dev/null +++ b/src/include/denc.h @@ -0,0 +1,1903 @@ +// -*- 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 + +#include "include/compat.h" +#include "include/intarith.h" +#include "include/int_types.h" +#include "include/scope_guard.h" + +#include "buffer.h" +#include "byteorder.h" + +#include "common/convenience.h" +#include "common/error_code.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; +}; + +template +inline constexpr bool denc_supported = denc_traits::supported; + + +// 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) + +template +class DencDumper { +public: + DencDumper(const char* name, + const ceph::bufferlist::contiguous_appender& appender) + : name{name}, + appender{appender}, + bl_offset{appender.bl.length()}, + space_offset{space_size()}, + start{appender.get_pos()} + {} + ~DencDumper() { + if (do_sample()) { + dump(); + } + } +private: + static bool do_sample() { + // this hackery with bits below is just to get a semi-reasonable + // distribution across time. it is somewhat exponential but not + // quite. + i++; + int bits = 0; + for (unsigned t = i; t; bits++) + t &= t - 1; + return bits <= 2; + } + size_t space_size() const { + return appender.get_logical_offset() - appender.get_out_of_band_offset(); + } + void dump() const { + char fn[PATH_MAX]; + ::snprintf(fn, sizeof(fn), + ENCODE_STRINGIFY(ENCODE_DUMP_PATH) "/%s__%d.%x", name, + getpid(), i++); + int fd = ::open(fn, O_WRONLY|O_TRUNC|O_CREAT|O_CLOEXEC|O_BINARY, 0644); + if (fd < 0) { + return; + } + auto close_fd = make_scope_guard([fd] { ::close(fd); }); + if (auto bl_delta = appender.bl.length() - bl_offset; bl_delta > 0) { + ceph::bufferlist dump_bl; + appender.bl.begin(bl_offset + space_offset).copy(bl_delta - space_offset, dump_bl); + const size_t space_len = space_size(); + dump_bl.append(appender.get_pos() - space_len, space_len); + dump_bl.write_fd(fd); + } else { + size_t len = appender.get_pos() - start; + [[maybe_unused]] int r = ::write(fd, start, len); + } + } + const char* name; + const ceph::bufferlist::contiguous_appender& appender; + const size_t bl_offset; + const size_t space_offset; + const char* start; + static int i; +}; + +template int DencDumper::i = 0; + +# define DENC_DUMP_PRE(Type) \ + DencDumper _denc_dumper{#Type, p}; +#else +# define DENC_DUMP_PRE(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, ceph::buffer::list::contiguous_appender& p, + uint64_t features=0); + inline void denc(T& o, ceph::buffer::ptr::const_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, ceph::buffer::list::contiguous_appender& p, + uint64_t features); + inline void denc(T& o, ceph::buffer::ptr::const_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, ceph::buffer::list::contiguous_appender& p, + uint64_t f=0); + static void decode(T& o, ceph::buffer::ptr::const_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, ceph::buffer::list::contiguous_appender& p, + uint64_t f); + static void decode(T& o, ceph::buffer::ptr::const_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, ceph::buffer::ptr, + and ceph::buffer::list 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(ceph::buffer::list::contiguous_appender& p) const; + void decode(ceph::buffer::ptr::const_iterator &p); + + or (for featured objects) + + void bound_encode(size_t& p, uint64_t f) const; + void encode(ceph::buffer::list::contiguous_appender& p, uint64_t f) const; + void decode(ceph::buffer::ptr::const_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 ceph::buffer::list is segmented. If you are + concerned about the cost, you might want to define yet another method: + + void decode(ceph::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 +inline constexpr bool is_any_of = (... || std::is_same_v); + +template struct underlying_type { + using type = T; +}; +template +struct underlying_type>> { + using type = std::underlying_type_t; +}; +template +using underlying_type_t = typename underlying_type::type; +} + +template +struct is_const_iterator + : std::conditional_t>, + std::true_type, + std::false_type> +{}; +template<> +struct is_const_iterator : std::false_type {}; +template<> +struct is_const_iterator : std::false_type { + // appender is used for *changing* the buffer +}; +template +inline constexpr bool is_const_iterator_v = is_const_iterator::value; + +template +std::enable_if_t, const T&> +get_pos_add(It& i) { + return *reinterpret_cast(i.get_pos_add(sizeof(T))); +} + +template +std::enable_if_t, T&> +get_pos_add(It& i) { + return *reinterpret_cast(i.get_pos_add(sizeof(T))); +} + +template +struct denc_traits< + T, + std::enable_if_t< + _denc::is_any_of<_denc::underlying_type_t, + ceph_le64, ceph_le32, ceph_le16, uint8_t +#ifndef _CHAR_IS_SIGNED + , int8_t +#endif + >>> { + 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); + } + template + static std::enable_if_t> + encode(const T &o, It& p, uint64_t f=0) { + get_pos_add(p) = o; + } + template + static std::enable_if_t> + decode(T& o, It& p, uint64_t f=0) { + o = get_pos_add(p); + } + static void decode(T& o, ceph::buffer::list::const_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 preferred to the ones defined using the specialized +// template, and hence get selected. This machinery 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 || + std::is_same_v>> { + using type = ceph_le16; +}; + +template +struct ExtType || + std::is_same_v>> { + using type = ceph_le32; +}; + +template +struct ExtType || + std::is_same_v>> { + using type = ceph_le64; +}; + +template<> +struct ExtType { + using type = uint8_t; +}; +template +using ExtType_t = typename ExtType::type; +} // namespace _denc + +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; + using etype = _denc::ExtType_t; + static void bound_encode(const T &o, size_t& p, uint64_t f=0) { + p += sizeof(etype); + } + template + static std::enable_if_t> + encode(const T &o, It& p, uint64_t f=0) { + get_pos_add(p) = o; + } + template + static std::enable_if_t> + decode(T& o, It &p, uint64_t f=0) { + o = get_pos_add(p); + } + static void decode(T& o, ceph::buffer::list::const_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, ceph::buffer::list::contiguous_appender& p) { + uint8_t byte = v & 0x7f; + v >>= 7; + while (v) { + byte |= 0x80; + get_pos_add<__u8>(p) = byte; + byte = (v & 0x7f); + v >>= 7; + } + get_pos_add<__u8>(p) = byte; +} + +template +inline void denc_varint(T& v, ceph::buffer::ptr::const_iterator& p) { + uint8_t byte = *(__u8*)p.get_pos_add(1); + v = byte & 0x7f; + int shift = 7; + while (byte & 0x80) { + byte = get_pos_add<__u8>(p); + 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; +} +template +inline std::enable_if_t> +denc_signed_varint(int64_t v, It& p) { + if (v < 0) { + v = (-v << 1) | 1; + } else { + v <<= 1; + } + denc_varint(v, p); +} + +template +inline std::enable_if_t> +denc_signed_varint(T& v, It& 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, + ceph::buffer::list::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, ceph::buffer::ptr::const_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; +} +template +inline std::enable_if_t> +denc_signed_varint_lowz(int64_t v, It& 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 std::enable_if_t> +denc_signed_varint_lowz(T& v, It& 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; +} + +template +inline std::enable_if_t> +denc_lba(uint64_t v, It& 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) { + *(ceph_le32*)p.get_pos_add(sizeof(uint32_t)) = word; + return; + } + word |= 0x80000000; + *(ceph_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; +} + +template +inline std::enable_if_t> +denc_lba(uint64_t& v, It& p) { + uint32_t word = *(ceph_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 std::enable_if_t denc( + const T& o, + size_t& p, + uint64_t f=0) +{ + if constexpr (traits::featured) { + traits::bound_encode(o, p, f); + } else { + traits::bound_encode(o, p); + } +} + +template> +inline std::enable_if_t> +denc(const T& o, + It& p, + uint64_t features=0) +{ + if constexpr (traits::featured) { + traits::encode(o, p, features); + } else { + traits::encode(o, p); + } +} + +template> +inline std::enable_if_t> +denc(T& o, + It& p, + uint64_t features=0) +{ + if constexpr (traits::featured) { + traits::decode(o, p, features); + } else { + traits::decode(o, p); + } +} + +namespace _denc { +template +struct has_legacy_denc : std::false_type {}; +template +struct has_legacy_denc() + .decode(std::declval< + ceph::buffer::list::const_iterator&>()))> + : std::true_type { + static void decode(T& v, ceph::buffer::list::const_iterator& p) { + v.decode(p); + } +}; +template +struct has_legacy_denc::need_contiguous>> : std::true_type { + static void decode(T& v, ceph::buffer::list::const_iterator& p) { + denc_traits::decode(v, p); + } +}; +} + +template, + typename has_legacy_denc=_denc::has_legacy_denc> +inline std::enable_if_t denc( + T& o, + ceph::buffer::list::const_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(); + } + template + static void encode(const value_type& s, + It& p, + uint64_t f=0) { + denc((uint32_t)s.size(), p); + memcpy(p.get_pos_add(s.size()), s.data(), s.size()); + } + template + static void decode(value_type& s, + It& p, + uint64_t f=0) { + uint32_t len; + denc(len, p); + decode_nohead(len, s, p); + } + static void decode(value_type& s, ceph::buffer::list::const_iterator& p) + { + uint32_t len; + denc(len, p); + decode_nohead(len, s, p); + } + template + static void decode_nohead(size_t len, value_type& s, It& p) { + s.clear(); + if (len) { + s.append(p.get_pos_add(len), len); + } + } + static void decode_nohead(size_t len, value_type& s, + ceph::buffer::list::const_iterator& p) { + if (len) { + if constexpr (std::is_same_v) { + s.clear(); + p.copy(len, s); + } else { + s.resize(len); + p.copy(len, s.data()); + } + } else { + s.clear(); + } + } + template + static std::enable_if_t> + encode_nohead(const value_type& s, It& p) { + auto len = s.length(); + maybe_inline_memcpy(p.get_pos_add(len), s.data(), len, 16); + } +}; + +// +// ceph::buffer::ptr +// +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 ceph::buffer::ptr& v, size_t& p, uint64_t f=0) { + p += sizeof(uint32_t) + v.length(); + } + template + static std::enable_if_t> + encode(const ceph::buffer::ptr& v, It& p, uint64_t f=0) { + denc((uint32_t)v.length(), p); + p.append(v); + } + template + static std::enable_if_t> + decode(ceph::buffer::ptr& v, It& p, uint64_t f=0) { + uint32_t len; + denc(len, p); + v = p.get_ptr(len); + } + static void decode(ceph::buffer::ptr& v, ceph::buffer::list::const_iterator& p) { + uint32_t len; + denc(len, p); + ceph::buffer::list s; + p.copy(len, s); + if (len) { + if (s.get_num_buffers() == 1) + v = s.front(); + else + v = ceph::buffer::copy(s.c_str(), s.length()); + } + } +}; + +// +// ceph::buffer::list +// +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 ceph::buffer::list& v, size_t& p, uint64_t f=0) { + p += sizeof(uint32_t) + v.length(); + } + static void encode(const ceph::buffer::list& v, ceph::buffer::list::contiguous_appender& p, + uint64_t f=0) { + denc((uint32_t)v.length(), p); + p.append(v); + } + static void decode(ceph::buffer::list& v, ceph::buffer::ptr::const_iterator& p, uint64_t f=0) { + uint32_t len; + denc(len, p); + v.clear(); + v.push_back(p.get_ptr(len)); + } + static void decode(ceph::buffer::list& v, ceph::buffer::list::const_iterator& p) { + uint32_t len; + denc(len, p); + v.clear(); + p.copy(len, v); + } + static void encode_nohead(const ceph::buffer::list& v, + ceph::buffer::list::contiguous_appender& p) { + p.append(v); + } + static void decode_nohead(size_t len, ceph::buffer::list& v, + ceph::buffer::ptr::const_iterator& p) { + v.clear(); + if (len) { + v.append(p.get_ptr(len)); + } + } + static void decode_nohead(size_t len, ceph::buffer::list& v, + ceph::buffer::list::const_iterator& p) { + v.clear(); + p.copy(len, v); + } +}; + +// +// std::pair +// +template +struct denc_traits< + std::pair, + std::enable_if_t> && denc_supported>> { + 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); + + static void bound_encode(const std::pair& v, size_t& p, uint64_t f = 0) { + if constexpr (featured) { + denc(v.first, p, f); + denc(v.second, p, f); + } else { + denc(v.first, p); + denc(v.second, p); + } + } + + static void encode(const std::pair& v, ceph::buffer::list::contiguous_appender& p, + uint64_t f = 0) { + if constexpr (featured) { + denc(v.first, p, f); + denc(v.second, p, f); + } else { + denc(v.first, p); + denc(v.second, p); + } + } + + static void decode(std::pair& v, ceph::buffer::ptr::const_iterator& p, uint64_t f=0) { + denc(const_cast&>(v.first), p, f); + denc(v.second, p, f); + } + template + static std::enable_if_t + decode(std::pair& v, ceph::buffer::list::const_iterator& p, + uint64_t f = 0) { + denc(const_cast&>(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 void bound_encode(const container& s, size_t& p, uint64_t f = 0) { + p += sizeof(uint32_t); + if constexpr (traits::bounded) { +#if _GLIBCXX_USE_CXX11_ABI + // intensionally not calling container's empty() method to not prohibit + // compiler from optimizing the check if it and the ::size() operate on + // different memory (observed when std::list::empty() works on pointers, + // not the size field). + if (const auto elem_num = s.size(); elem_num > 0) { +#else + if (!s.empty()) { + const auto elem_num = s.size(); +#endif + // STL containers use weird element types like std::pair; + // cast to something we have denc_traits for. + size_t elem_size = 0; + if constexpr (traits::featured) { + denc(static_cast(*s.begin()), elem_size, f); + } else { + denc(static_cast(*s.begin()), elem_size); + } + p += elem_size * elem_num; + } + } else { + for (const T& e : s) { + if constexpr (traits::featured) { + denc(e, p, f); + } else { + denc(e, p); + } + } + } + } + + template + static void encode(const container& s, + ceph::buffer::list::contiguous_appender& p, + uint64_t f = 0) { + denc((uint32_t)s.size(), p); + if constexpr (traits::featured) { + encode_nohead(s, p, f); + } else { + encode_nohead(s, p); + } + } + static void decode(container& s, ceph::buffer::ptr::const_iterator& p, + uint64_t f = 0) { + uint32_t num; + denc(num, p); + decode_nohead(num, s, p, f); + } + template + static std::enable_if_t + decode(container& s, ceph::buffer::list::const_iterator& p) { + uint32_t num; + denc(num, p); + decode_nohead(num, s, p); + } + + // nohead + static void encode_nohead(const container& s, ceph::buffer::list::contiguous_appender& p, + uint64_t f = 0) { + for (const T& e : s) { + if constexpr (traits::featured) { + denc(e, p, f); + } else { + denc(e, p); + } + } + } + static void decode_nohead(size_t num, container& s, + ceph::buffer::ptr::const_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 std::enable_if_t + decode_nohead(size_t num, container& s, + ceph::buffer::list::const_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 + inline constexpr bool container_has_reserve_v = + container_has_reserve::value; + + + template + struct container_details_base { + using T = typename Container::value_type; + static void reserve(Container& c, size_t s) { + if constexpr (container_has_reserve_v) { + c.reserve(s); + } + } + }; + + 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_t::supported>> + : public _denc::container_base>, + T, Ts...> {}; + +template +struct denc_traits< + std::vector, + typename std::enable_if_t::supported>> + : public _denc::container_base>, + T, Ts...> {}; + +template +struct denc_traits< + boost::container::small_vector, + typename std::enable_if_t::supported>> { +private: + using container = boost::container::small_vector; +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 void bound_encode(const container& s, size_t& p, uint64_t f = 0) { + p += sizeof(uint32_t); + if constexpr (traits::bounded) { + if (!s.empty()) { + const auto elem_num = s.size(); + size_t elem_size = 0; + if constexpr (traits::featured) { + denc(*s.begin(), elem_size, f); + } else { + denc(*s.begin(), elem_size); + } + p += elem_size * elem_num; + } + } else { + for (const T& e : s) { + if constexpr (traits::featured) { + denc(e, p, f); + } else { + denc(e, p); + } + } + } + } + + template + static void encode(const container& s, + ceph::buffer::list::contiguous_appender& p, + uint64_t f = 0) { + denc((uint32_t)s.size(), p); + if constexpr (traits::featured) { + encode_nohead(s, p, f); + } else { + encode_nohead(s, p); + } + } + static void decode(container& s, ceph::buffer::ptr::const_iterator& p, + uint64_t f = 0) { + uint32_t num; + denc(num, p); + decode_nohead(num, s, p, f); + } + template + static std::enable_if_t + decode(container& s, ceph::buffer::list::const_iterator& p) { + uint32_t num; + denc(num, p); + decode_nohead(num, s, p); + } + + // nohead + static void encode_nohead(const container& s, ceph::buffer::list::contiguous_appender& p, + uint64_t f = 0) { + for (const T& e : s) { + if constexpr (traits::featured) { + denc(e, p, f); + } else { + denc(e, p); + } + } + } + static void decode_nohead(size_t num, container& s, + ceph::buffer::ptr::const_iterator& p, + uint64_t f=0) { + s.clear(); + s.reserve(num); + while (num--) { + T t; + denc(t, p, f); + s.push_back(std::move(t)); + } + } + template + static std::enable_if_t + decode_nohead(size_t num, container& s, + ceph::buffer::list::const_iterator& p) { + s.clear(); + s.reserve(num); + while (num--) { + T t; + denc(t, p); + s.push_back(std::move(t)); + } + } +}; + +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, + std::enable_if_t::supported>> + : public _denc::container_base>, + T, Ts...> {}; + +template +struct denc_traits< + boost::container::flat_set, + std::enable_if_t::supported>> + : 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 = 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::map, + std::enable_if_t::supported && + denc_traits::supported>> + : public _denc::container_base>, + A, B, Ts...> {}; + +template +struct denc_traits< + boost::container::flat_map, + std::enable_if_t::supported && + denc_traits::supported>> + : public _denc::container_base< + boost::container::flat_map, + _denc::maplike_details>, + A, B, Ts...> {}; + +template +struct denc_traits< + std::array, + std::enable_if_t::supported>> { +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; + + static void bound_encode(const container& s, size_t& p, uint64_t f = 0) { + if constexpr (traits::bounded) { + if constexpr (traits::featured) { + if (!s.empty()) { + size_t elem_size = 0; + denc(*s.begin(), elem_size, f); + p += elem_size * s.size(); + } + } else { + size_t elem_size = 0; + denc(*s.begin(), elem_size); + p += elem_size * N; + } + } else { + for (const auto& e : s) { + if constexpr (traits::featured) { + denc(e, p, f); + } else { + denc(e, p); + } + } + } + } + + static void encode(const container& s, ceph::buffer::list::contiguous_appender& p, + uint64_t f = 0) { + for (const auto& e : s) { + if constexpr (traits::featured) { + denc(e, p, f); + } else { + denc(e, p); + } + } + } + static void decode(container& s, ceph::buffer::ptr::const_iterator& p, + uint64_t f = 0) { + for (auto& e : s) + denc(e, p, f); + } + template + static std::enable_if_t + decode(container& s, ceph::buffer::list::const_iterator& p) { + for (auto& e : s) { + denc(e, p); + } + } +}; + +template +struct denc_traits< + std::tuple, + std::enable_if_t<(denc_traits::supported && ...)>> { + +private: + static_assert(sizeof...(Ts) > 0, + "Zero-length tuples are not supported."); + using container = std::tuple; + +public: + + static constexpr bool supported = true; + static constexpr bool featured = (denc_traits::featured || ...); + static constexpr bool bounded = (denc_traits::bounded && ...); + static constexpr bool need_contiguous = + (denc_traits::need_contiguous || ...); + + template + static std::enable_if_t::featured> + bound_encode(const container& s, size_t& p, uint64_t f) { + ceph::for_each(s, [&p, f] (const auto& e) { + if constexpr (denc_traits>::featured) { + denc(e, p, f); + } else { + denc(e, p); + } + }); + } + template + static std::enable_if_t::featured> + bound_encode(const container& s, size_t& p) { + ceph::for_each(s, [&p] (const auto& e) { + denc(e, p); + }); + } + + template + static std::enable_if_t::featured> + encode(const container& s, ceph::buffer::list::contiguous_appender& p, + uint64_t f) { + ceph::for_each(s, [&p, f] (const auto& e) { + if constexpr (denc_traits>::featured) { + denc(e, p, f); + } else { + denc(e, p); + } + }); + } + template + static std::enable_if_t::featured> + encode(const container& s, ceph::buffer::list::contiguous_appender& p) { + ceph::for_each(s, [&p] (const auto& e) { + denc(e, p); + }); + } + + static void decode(container& s, ceph::buffer::ptr::const_iterator& p, + uint64_t f = 0) { + ceph::for_each(s, [&p] (auto& e) { + denc(e, p); + }); + } + + template + static std::enable_if_t::need_contiguous> + decode(container& s, ceph::buffer::list::const_iterator& p, uint64_t f = 0) { + ceph::for_each(s, [&p] (auto& e) { + denc(e, p); + }); + } +}; + +// +// boost::optional +// +template +struct denc_traits< + boost::optional, + std::enable_if_t::supported>> { + 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; + + static void bound_encode(const boost::optional& v, size_t& p, + uint64_t f = 0) { + p += sizeof(bool); + if (v) { + if constexpr (featured) { + denc(*v, p, f); + } else { + denc(*v, p); + } + } + } + + static void encode(const boost::optional& v, + ceph::buffer::list::contiguous_appender& p, + uint64_t f = 0) { + denc((bool)v, p); + if (v) { + if constexpr (featured) { + denc(*v, p, f); + } else { + denc(*v, p); + } + } + } + + static void decode(boost::optional& v, ceph::buffer::ptr::const_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 std::enable_if_t + decode(boost::optional& v, ceph::buffer::list::const_iterator& p) { + bool x; + denc(x, p); + if (x) { + v = T{}; + denc(*v, p); + } else { + v = boost::none; + } + } + + template + static void encode_nohead(const boost::optional& v, + ceph::buffer::list::contiguous_appender& p, + uint64_t f = 0) { + if (v) { + if constexpr (featured) { + denc(*v, p, f); + } else { + denc(*v, p); + } + } + } + + static void decode_nohead(bool num, boost::optional& v, + ceph::buffer::ptr::const_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, + ceph::buffer::list::contiguous_appender& p) { + denc(false, p); + } +}; + +// +// std::optional +// +template +struct denc_traits< + std::optional, + std::enable_if_t::supported>> { + 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; + + static void bound_encode(const std::optional& v, size_t& p, + uint64_t f = 0) { + p += sizeof(bool); + if (v) { + if constexpr (featured) { + denc(*v, p, f); + } else { + denc(*v, p); + } + } + } + + static void encode(const std::optional& v, + ceph::buffer::list::contiguous_appender& p, + uint64_t f = 0) { + denc((bool)v, p); + if (v) { + if constexpr (featured) { + denc(*v, p, f); + } else { + denc(*v, p); + } + } + } + + static void decode(std::optional& v, ceph::buffer::ptr::const_iterator& p, + uint64_t f = 0) { + bool x; + denc(x, p, f); + if (x) { + v = T{}; + denc(*v, p, f); + } else { + v = std::nullopt; + } + } + + template + static std::enable_if_t + decode(std::optional& v, ceph::buffer::list::const_iterator& p) { + bool x; + denc(x, p); + if (x) { + v = T{}; + denc(*v, p); + } else { + v = std::nullopt; + } + } + + static void encode_nohead(const std::optional& v, + ceph::buffer::list::contiguous_appender& p, + uint64_t f = 0) { + if (v) { + if constexpr (featured) { + denc(*v, p, f); + } else { + denc(*v, p); + } + } + } + + static void decode_nohead(bool num, std::optional& v, + ceph::buffer::ptr::const_iterator& p, uint64_t f = 0) { + if (num) { + v = T(); + denc(*v, p, f); + } else { + v = std::nullopt; + } + } +}; + +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 std::nullopt_t& v, size_t& p) { + p += sizeof(bool); + } + + static void encode(const std::nullopt_t& v, + ceph::buffer::list::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, ::ceph::buffer::list::contiguous_appender& p, \ + uint64_t f=0) { \ + v.encode(p); \ + } \ + static void decode(T& v, ::ceph::buffer::ptr::const_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, ::ceph::buffer::list::contiguous_appender& p, \ + uint64_t f) { \ + v.encode(p, f); \ + } \ + static void decode(T& v, ::ceph::buffer::ptr::const_iterator& p, uint64_t f=0) { \ + v.decode(p, f); \ + } \ + }; + +// ---------------------------------------------------------------------- +// encoded_sizeof_wrapper + +namespace ceph { + +template > +constexpr std::enable_if_t +encoded_sizeof_bounded() { + size_t p = 0; + traits::bound_encode(T(), p); + return p; +} + +template > +std::enable_if_t +encoded_sizeof(const T &t) { + size_t p = 0; + traits::bound_encode(t, p); + return p; +} + +} // namespace ceph + + +// ---------------------------------------------------------------------- +// 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). + +namespace ceph { +template> +inline std::enable_if_t encode( + const T& o, + ceph::buffer::list& 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 std::enable_if_t encode( + const T& o, ::ceph::buffer::list& 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 std::enable_if_t decode( + T& o, + ::ceph::buffer::list::const_iterator& p) +{ + if (p.end()) + throw ::ceph::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.is_pointing_same_raw(bl.back()) && remaining > CEPH_PAGE_SIZE) { + traits::decode(o, p); + } else { + // ensure we get a contigous buffer... until the end of the + // ceph::buffer::list. 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. + ceph::buffer::ptr tmp; + auto t = p; + t.copy_shallow(remaining, tmp); + auto cp = std::cbegin(tmp); + traits::decode(o, cp); + p += cp.get_offset(); + } +} + +template> +inline std::enable_if_t decode( + T& o, + ceph::buffer::list::const_iterator& p) +{ + if (p.end()) + throw ceph::buffer::end_of_buffer(); + // ensure we get a contigous buffer... until the end of the + // ceph::buffer::list. 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. + ceph::buffer::ptr tmp; + auto t = p; + t.copy_shallow(p.get_bl().length() - p.get_off(), tmp); + auto cp = std::cbegin(tmp); + traits::decode(o, cp); + p += cp.get_offset(); +} + +// nohead variants +template> +inline std::enable_if_t encode_nohead( + const T& o, + ceph::buffer::list& bl) +{ + size_t len = 0; + traits::bound_encode(o, len); + auto a = bl.get_contiguous_appender(len); + traits::encode_nohead(o, a); +} + +template> +inline std::enable_if_t decode_nohead( + size_t num, + T& o, + ceph::buffer::list::const_iterator& p) +{ + if (!num) + return; + if (p.end()) + throw ceph::buffer::end_of_buffer(); + if constexpr (traits::need_contiguous) { + ceph::buffer::ptr tmp; + auto t = p; + if constexpr (denc_traits::bounded) { + size_t element_size = 0; + typename T::value_type v; + denc_traits::bound_encode(v, element_size); + t.copy_shallow(num * element_size, tmp); + } else { + t.copy_shallow(p.get_bl().length() - p.get_off(), tmp); + } + auto cp = std::cbegin(tmp); + traits::decode_nohead(num, o, cp); + p += cp.get_offset(); + } else { + traits::decode_nohead(num, o, p); + } +} +} + + +// ---------------------------------------------------------------- +// 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(::ceph::buffer::list::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(::ceph::buffer::list::contiguous_appender& p, \ + __u8 *struct_v, \ + __u8 *struct_compat, \ + char **len_pos, \ + uint32_t *start_oob_off) { \ + *(ceph_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(::ceph::buffer::ptr::const_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(::ceph::buffer::ptr::const_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; \ + if (pos > end) { \ + throw ::ceph::buffer::malformed_input(__PRETTY_FUNCTION__); \ + } \ + if (pos < end) { \ + p += 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(::ceph::buffer::list::contiguous_appender& p) const { \ + DENC_DUMP_PRE(Type); \ + _denc_friend(*this, p); \ + } \ + void decode(::ceph::buffer::ptr::const_iterator& p) { \ + _denc_friend(*this, p); \ + } \ + template \ + friend std::enable_if_t || \ + std::is_same_v> \ + _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(::ceph::buffer::list::contiguous_appender& p, uint64_t f) const { \ + DENC_DUMP_PRE(Type); \ + _denc_friend(*this, p, f); \ + } \ + void decode(::ceph::buffer::ptr::const_iterator& p, uint64_t f=0) { \ + _denc_friend(*this, p, f); \ + } \ + template \ + friend std::enable_if_t || \ + std::is_same_v> \ + _denc_friend(T& v, P& p, uint64_t f) + +#endif -- cgit v1.2.3