1    	// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2    	// vim: ts=8 sw=2 smarttab
3    	/*
4    	 * Ceph - scalable distributed file system
5    	 *
6    	 * Copyright (C) 2016 Allen Samuels <allen.samuels@sandisk.com>
7    	 *
8    	 * This is free software; you can redistribute it and/or
9    	 * modify it under the terms of the GNU Lesser General Public
10   	 * License version 2.1, as published by the Free Software
11   	 * Foundation.  See file COPYING.
12   	 *
13   	 */
14   	
15   	// If you #include "include/encoding.h" you get the old-style *and*
16   	// the new-style definitions.  (The old-style needs denc_traits<> in
17   	// order to disable the container helpers when new-style traits are
18   	// present.)
19   	
20   	// You can also just #include "include/denc.h" and get only the
21   	// new-style helpers.  The eventual goal is to drop the legacy
22   	// definitions.
23   	
24   	#ifndef _ENC_DEC_H
25   	#define _ENC_DEC_H
26   	
27   	#include <array>
28   	#include <cstring>
29   	#include <map>
30   	#include <optional>
31   	#include <set>
32   	#include <string>
33   	#include <type_traits>
34   	#include <vector>
35   	
36   	#include <boost/container/flat_map.hpp>
37   	#include <boost/container/flat_set.hpp>
38   	#include <boost/intrusive/set.hpp>
39   	#include <boost/optional.hpp>
40   	
41   	#include "include/ceph_assert.h"	// boost clobbers this
42   	#include "include/intarith.h"
43   	#include "include/int_types.h"
44   	#include "include/scope_guard.h"
45   	
46   	#include "buffer.h"
47   	#include "byteorder.h"
48   	
49   	#include "common/convenience.h"
50   	
51   	template<typename T, typename=void>
52   	struct denc_traits {
53   	  static constexpr bool supported = false;
54   	  static constexpr bool featured = false;
55   	  static constexpr bool bounded = false;
56   	  static constexpr bool need_contiguous = true;
57   	};
58   	
59   	template<typename T>
60   	inline constexpr bool denc_supported = denc_traits<T>::supported;
61   	
62   	
63   	// hack for debug only; FIXME
64   	//#include <iostream>
65   	//using std::cout;
66   	
67   	// Define this to compile in a dump of all encoded objects to disk to
68   	// populate ceph-object-corpus.  Note that there is an almost
69   	// identical implementation in encoding.h, but you only need to define
70   	// ENCODE_DUMP_PATH here.
71   	//
72   	// See src/test/encoding/generate-corpus-objects.sh.
73   	//
74   	//#define ENCODE_DUMP_PATH /tmp/something
75   	
76   	#ifdef ENCODE_DUMP_PATH
77   	# include <cstdio>
78   	# include <sys/types.h>
79   	# include <sys/stat.h>
80   	# include <fcntl.h>
81   	
82   	# define ENCODE_STR(x) #x
83   	# define ENCODE_STRINGIFY(x) ENCODE_STR(x)
84   	
85   	template<typename T>
86   	class DencDumper {
87   	public:
88   	  DencDumper(const char* name,
89   		     const ceph::bufferlist::contiguous_appender& appender)
90   	    : name{name},
91   	      appender{appender},
92   	      bl_offset{appender.bl.length()},
93   	      space_offset{space_size()},
94   	      start{appender.get_pos()}
95   	  {}
96   	  ~DencDumper() {
97   	    if (do_sample()) {
98   	      dump();
99   	    }
100  	  }
101  	private:
102  	  static bool do_sample() {
103  	    // this hackery with bits below is just to get a semi-reasonable
104  	    // distribution across time.  it is somewhat exponential but not
105  	    // quite.
106  	    i++;
107  	    int bits = 0;
108  	    for (unsigned t = i; t; bits++)
109  	      t &= t - 1;
110  	    return bits <= 2;
111  	  }
112  	  size_t space_size() const {
113  	    return appender.get_logical_offset() - appender.get_out_of_band_offset();
114  	  }
115  	  void dump() const {
116  	    char fn[PATH_MAX];
117  	    ::snprintf(fn, sizeof(fn),
118  		       ENCODE_STRINGIFY(ENCODE_DUMP_PATH) "/%s__%d.%x", name,
119  		       getpid(), i++);
120  	    int fd = ::open(fn, O_WRONLY|O_TRUNC|O_CREAT|O_CLOEXEC, 0644);
121  	    if (fd < 0) {
122  	      return;
123  	    }
124  	    auto close_fd = make_scope_guard([fd] { ::close(fd); });
125  	    if (auto bl_delta = appender.bl.length() - bl_offset; bl_delta > 0) {
126  	      ceph::bufferlist dump_bl;
127  	      appender.bl.copy(bl_offset + space_offset, bl_delta - space_offset, dump_bl);
128  	      const size_t space_len = space_size();
129  	      dump_bl.append(appender.get_pos() - space_len, space_len);
130  	      dump_bl.write_fd(fd);
131  	    } else {
132  	      size_t len = appender.get_pos() - start;
133  	      [[maybe_unused]] int r = ::write(fd, start, len);
134  	    }
135  	  }
136  	  const char* name;
137  	  const ceph::bufferlist::contiguous_appender& appender;
138  	  const size_t bl_offset;
139  	  const size_t space_offset;
140  	  const char* start;
141  	  static int i;
142  	};
143  	
144  	template<typename T> int DencDumper<T>::i = 0;
145  	
146  	# define DENC_DUMP_PRE(Type)						\
147  	  DencDumper<Type> _denc_dumper{#Type, p};
148  	#else
149  	# define DENC_DUMP_PRE(Type)
150  	#endif
151  	
152  	
153  	/*
154  	
155  	  top level level functions look like so
156  	  ======================================
157  	
158  	    inline void denc(const T& o, size_t& p, uint64_t features=0);
159  	    inline void denc(const T& o, ceph::buffer::list::contiguous_appender& p,
160  	                     uint64_t features=0);
161  	    inline void denc(T& o, ceph::buffer::ptr::const_iterator& p, uint64_t features=0);
162  	
163  	  or (for featured objects)
164  	
165  	    inline void denc(const T& o, size_t& p, uint64_t features);
166  	    inline void denc(const T& o, ceph::buffer::list::contiguous_appender& p,
167  	                     uint64_t features);
168  	    inline void denc(T& o, ceph::buffer::ptr::const_iterator& p, uint64_t features);
169  	
170  	  - These are symmetrical, so that they can be used from the magic DENC
171  	  method of writing the bound_encode/encode/decode methods all in one go;
172  	  they differ only in the type of p.
173  	
174  	  - These are automatically fabricated via a template that calls into
175  	  the denc_traits<> methods (see below), provided denc_traits<T>::supported
176  	  is defined and true.  They never need to be written explicitly.
177  	
178  	
179  	  static denc_traits<> definitions look like so
180  	  =============================================
181  	
182  	    template<>
183  	    struct denc_traits<T> {
184  	      static constexpr bool supported = true;
185  	      static constexpr bool bounded = false;
186  	      static constexpr bool featured = false;
187  	      static constexpr bool need_contiguous = true;
188  	      static void bound_encode(const T &o, size_t& p, uint64_t f=0);
189  	      static void encode(const T &o, ceph::buffer::list::contiguous_appender& p,
190  			         uint64_t f=0);
191  	      static void decode(T& o, ceph::buffer::ptr::const_iterator &p, uint64_t f=0);
192  	    };
193  	
194  	  or (for featured objects)
195  	
196  	    template<>
197  	    struct denc_traits<T> {
198  	      static constexpr bool supported = true;
199  	      static constexpr bool bounded = false;
200  	      static constexpr bool featured = true;
201  	      static constexpr bool need_contiguous = true;
202  	      static void bound_encode(const T &o, size_t& p, uint64_t f);
203  	      static void encode(const T &o, ceph::buffer::list::contiguous_appender& p,
204  			         uint64_t f);
205  	      static void decode(T& o, ceph::buffer::ptr::const_iterator &p, uint64_t f=0);
206  	    };
207  	
208  	  - denc_traits<T> is normally declared via the WRITE_CLASS_DENC(type) macro,
209  	  which is used in place of the old-style WRITE_CLASS_ENCODER(type) macro.
210  	  There are _FEATURED and _BOUNDED variants.  The class traits simply call
211  	  into class methods of the same name (see below).
212  	
213  	  - denc_traits<T> can also be written explicitly for some type to indicate
214  	  how it should be encoded.  This is the "source of truth" for how a type
215  	  is encoded.
216  	
217  	  - denc_traits<T> are declared for the base integer types, string, ceph::buffer::ptr,
218  	  and ceph::buffer::list base types.
219  	
220  	  - denc_traits<std::foo<T>>-like traits are declared for standard container
221  	  types.
222  	
223  	
224  	  class methods look like so
225  	  ==========================
226  	
227  	    void bound_encode(size_t& p) const;
228  	    void encode(ceph::buffer::list::contiguous_appender& p) const;
229  	    void decode(ceph::buffer::ptr::const_iterator &p);
230  	
231  	  or (for featured objects)
232  	
233  	    void bound_encode(size_t& p, uint64_t f) const;
234  	    void encode(ceph::buffer::list::contiguous_appender& p, uint64_t f) const;
235  	    void decode(ceph::buffer::ptr::const_iterator &p);
236  	
237  	  - These are normally invoked by the denc_traits<> methods that are
238  	  declared via WRITE_CLASS_DENC, although you can also invoke them explicitly
239  	  in your code.
240  	
241  	  - These methods are optimised for contiguous buffer, but denc() will try
242  	    rebuild a contigous one if the decoded ceph::buffer::list is segmented. If you are
243  	    concerned about the cost, you might want to define yet another method:
244  	
245  	    void decode(ceph::buffer::list::iterator &p);
246  	
247  	  - These can be defined either explicitly (as above), or can be "magically"
248  	  defined all in one go using the DENC macro and DENC_{START,FINISH} helpers
249  	  (which work like the legacy {ENCODE,DECODE}_{START,FINISH} macros):
250  	
251  	    class foo_t {
252  	      ...
253  	      DENC(foo_t, v, p) {
254  	        DENC_START(1, 1, p);
255  	        denc(v.foo, p);
256  	        denc(v.bar, p);
257  	        denc(v.baz, p);
258  	        DENC_FINISH(p);
259  	      }
260  	      ...
261  	    };
262  	    WRITE_CLASS_DENC(foo_t)
263  	
264  	  */
265  	
266  	// ---------------------------------------------------------------------
267  	// raw types
268  	namespace _denc {
269  	template<typename T, typename... Us>
270  	inline constexpr bool is_any_of = (... || std::is_same_v<T, Us>);
271  	
272  	template<typename T, typename=void> struct underlying_type {
273  	  using type = T;
274  	};
275  	template<typename T>
276  	struct underlying_type<T, std::enable_if_t<std::is_enum_v<T>>> {
277  	  using type = std::underlying_type_t<T>;
278  	};
279  	template<typename T>
280  	using underlying_type_t = typename underlying_type<T>::type;
281  	}
282  	
283  	template<class It>
284  	struct is_const_iterator
285  	  : std::conditional_t<std::is_const_v<std::remove_pointer_t<typename It::pointer>>,
286  			       std::true_type,
287  			       std::false_type>
288  	{};
289  	template<>
290  	struct is_const_iterator<size_t> : std::false_type {};
291  	template<>
292  	struct is_const_iterator<ceph::buffer::list::contiguous_appender> : std::false_type {
293  	  // appender is used for *changing* the buffer
294  	};
295  	template<class It>
296  	inline constexpr bool is_const_iterator_v = is_const_iterator<It>::value;
297  	
298  	template<typename T, class It>
299  	std::enable_if_t<is_const_iterator_v<It>, const T&>
300  	get_pos_add(It& i) {
301  	  return *reinterpret_cast<const T*>(i.get_pos_add(sizeof(T)));
302  	}
303  	
304  	template<typename T, class It>
305  	std::enable_if_t<!is_const_iterator_v<It>, T&>
306  	get_pos_add(It& i) {
307  	  return *reinterpret_cast<T*>(i.get_pos_add(sizeof(T)));
308  	}
309  	
310  	template<typename T>
311  	struct denc_traits<
312  	  T,
313  	  std::enable_if_t<
314  	    _denc::is_any_of<_denc::underlying_type_t<T>,
315  			     ceph_le64, ceph_le32, ceph_le16, uint8_t
316  	#ifndef _CHAR_IS_SIGNED
317  			       , int8_t
318  	#endif
319  			     >>> {
320  	  static constexpr bool supported = true;
321  	  static constexpr bool featured = false;
322  	  static constexpr bool bounded = true;
323  	  static constexpr bool need_contiguous = false;
324  	  static void bound_encode(const T &o, size_t& p, uint64_t f=0) {
325  	    p += sizeof(T);
326  	  }
327  	  template<class It>
328  	  static std::enable_if_t<!is_const_iterator_v<It>>
329  	  encode(const T &o, It& p, uint64_t f=0) {
330  	    get_pos_add<T>(p) = o;
331  	  }
332  	  template<class It>
333  	  static std::enable_if_t<is_const_iterator_v<It>>
334  	  decode(T& o, It& p, uint64_t f=0) {
335  	    o = get_pos_add<T>(p);
336  	  }
337  	  static void decode(T& o, ceph::buffer::list::const_iterator &p) {
338  	    p.copy(sizeof(T), reinterpret_cast<char*>(&o));
339  	  }
340  	};
341  	
342  	
343  	// -----------------------------------------------------------------------
344  	// integer types
345  	
346  	// itype == internal type
347  	// otype == external type, i.e., the type on the wire
348  	
349  	// NOTE: the overload resolution ensures that the legacy encode/decode methods
350  	// defined for int types is preferred to the ones  defined using the specialized
351  	// template, and hence get selected. This machinery prevents these these from
352  	// getting glued into the legacy encode/decode methods; the overhead of setting
353  	// up a contiguous_appender etc is likely to be slower.
354  	namespace _denc {
355  	
356  	template<typename T, typename=void> struct ExtType {
357  	  using type = void;
358  	};
359  	
360  	template<typename T>
361  	struct ExtType<T, std::enable_if_t<std::is_same_v<T, int16_t> ||
362  					   std::is_same_v<T, uint16_t>>> {
363  	  using type = ceph_le16;
364  	};
365  	
366  	template<typename T>
367  	struct ExtType<T, std::enable_if_t<std::is_same_v<T, int32_t> ||
368  					   std::is_same_v<T, uint32_t>>> {
369  	  using type = ceph_le32;
370  	};
371  	
372  	template<typename T>
373  	struct ExtType<T, std::enable_if_t<std::is_same_v<T, int64_t> ||
374  					   std::is_same_v<T, uint64_t>>> {
375  	  using type = ceph_le64;
376  	};
377  	
378  	template<>
379  	struct ExtType<bool> {
380  	  using type = uint8_t;
381  	};
382  	template<typename T>
383  	using ExtType_t = typename ExtType<T>::type;
384  	} // namespace _denc
385  	
386  	template<typename T>
387  	struct denc_traits<T, std::enable_if_t<!std::is_void_v<_denc::ExtType_t<T>>>>
388  	{
389  	  static constexpr bool supported = true;
390  	  static constexpr bool featured = false;
391  	  static constexpr bool bounded = true;
392  	  static constexpr bool need_contiguous = false;
393  	  using etype = _denc::ExtType_t<T>;
394  	  static void bound_encode(const T &o, size_t& p, uint64_t f=0) {
395  	    p += sizeof(etype);
396  	  }
397  	  template<class It>
398  	  static std::enable_if_t<!is_const_iterator_v<It>>
399  	  encode(const T &o, It& p, uint64_t f=0) {
400  	    get_pos_add<etype>(p) = o;
401  	  }
402  	  template<class It>
403  	  static std::enable_if_t<is_const_iterator_v<It>>
404  	  decode(T& o, It &p, uint64_t f=0) {
405  	    o = get_pos_add<etype>(p);
406  	  }
407  	  static void decode(T& o, ceph::buffer::list::const_iterator &p) {
408  	    etype e;
409  	    p.copy(sizeof(etype), reinterpret_cast<char*>(&e));
410  	    o = e;
411  	  }
412  	};
413  	
414  	// varint
415  	//
416  	// high bit of each byte indicates another byte follows.
417  	template<typename T>
418  	inline void denc_varint(T v, size_t& p) {
419  	  p += sizeof(T) + 1;
420  	}
421  	
422  	template<typename T>
423  	inline void denc_varint(T v, ceph::buffer::list::contiguous_appender& p) {
424  	  uint8_t byte = v & 0x7f;
425  	  v >>= 7;
426  	  while (v) {
427  	    byte |= 0x80;
428  	    get_pos_add<__u8>(p) = byte;
429  	    byte = (v & 0x7f);
430  	    v >>= 7;
431  	  }
432  	  get_pos_add<__u8>(p) = byte;
433  	}
434  	
435  	template<typename T>
436  	inline void denc_varint(T& v, ceph::buffer::ptr::const_iterator& p) {
437  	  uint8_t byte = *(__u8*)p.get_pos_add(1);
438  	  v = byte & 0x7f;
439  	  int shift = 7;
440  	  while (byte & 0x80) {
441  	    byte = get_pos_add<__u8>(p);
442  	    v |= (T)(byte & 0x7f) << shift;
443  	    shift += 7;
444  	  }
445  	}
446  	
447  	
448  	// signed varint encoding
449  	//
450  	// low bit = 1 = negative, 0 = positive
451  	// high bit of every byte indicates whether another byte follows.
452  	inline void denc_signed_varint(int64_t v, size_t& p) {
453  	  p += sizeof(v) + 2;
454  	}
455  	template<class It>
456  	inline std::enable_if_t<!is_const_iterator_v<It>>
457  	denc_signed_varint(int64_t v, It& p) {
458  	  if (v < 0) {
459  	    v = (-v << 1) | 1;
460  	  } else {
461  	    v <<= 1;
462  	  }
463  	  denc_varint(v, p);
464  	}
465  	
466  	template<typename T, class It>
467  	inline std::enable_if_t<is_const_iterator_v<It>>
468  	denc_signed_varint(T& v, It& p)
469  	{
470  	  int64_t i = 0;
471  	  denc_varint(i, p);
472  	  if (i & 1) {
473  	    v = -(i >> 1);
474  	  } else {
475  	    v = i >> 1;
476  	  }
477  	}
478  	
479  	// varint + lowz encoding
480  	//
481  	// first(low) 2 bits = how many low zero bits (nibbles)
482  	// high bit of each byte = another byte follows
483  	// (so, 5 bits data in first byte, 7 bits data thereafter)
484  	inline void denc_varint_lowz(uint64_t v, size_t& p) {
485  	  p += sizeof(v) + 2;
486  	}
487  	inline void denc_varint_lowz(uint64_t v,
488  				     ceph::buffer::list::contiguous_appender& p) {
489  	  int lowznib = v ? (ctz(v) / 4) : 0;
490  	  if (lowznib > 3)
491  	    lowznib = 3;
492  	  v >>= lowznib * 4;
493  	  v <<= 2;
494  	  v |= lowznib;
495  	  denc_varint(v, p);
496  	}
497  	
498  	template<typename T>
499  	inline void denc_varint_lowz(T& v, ceph::buffer::ptr::const_iterator& p)
500  	{
501  	  uint64_t i = 0;
502  	  denc_varint(i, p);
503  	  int lowznib = (i & 3);
504  	  i >>= 2;
505  	  i <<= lowznib * 4;
506  	  v = i;
507  	}
508  	
509  	// signed varint + lowz encoding
510  	//
511  	// first low bit = 1 for negative, 0 for positive
512  	// next 2 bits = how many low zero bits (nibbles)
513  	// high bit of each byte = another byte follows
514  	// (so, 4 bits data in first byte, 7 bits data thereafter)
515  	inline void denc_signed_varint_lowz(int64_t v, size_t& p) {
516  	  p += sizeof(v) + 2;
517  	}
518  	template<class It>
519  	inline std::enable_if_t<!is_const_iterator_v<It>>
520  	denc_signed_varint_lowz(int64_t v, It& p) {
521  	  bool negative = false;
522  	  if (v < 0) {
523  	    v = -v;
524  	    negative = true;
525  	  }
526  	  unsigned lowznib = v ? (ctz(v) / 4) : 0u;
527  	  if (lowznib > 3)
528  	    lowznib = 3;
529  	  v >>= lowznib * 4;
530  	  v <<= 3;
531  	  v |= lowznib << 1;
532  	  v |= (int)negative;
533  	  denc_varint(v, p);
534  	}
535  	
536  	template<typename T, class It>
537  	inline std::enable_if_t<is_const_iterator_v<It>>
538  	denc_signed_varint_lowz(T& v, It& p)
539  	{
540  	  int64_t i = 0;
541  	  denc_varint(i, p);
542  	  int lowznib = (i & 6) >> 1;
543  	  if (i & 1) {
544  	    i >>= 3;
545  	    i <<= lowznib * 4;
546  	    v = -i;
547  	  } else {
548  	    i >>= 3;
549  	    i <<= lowznib * 4;
550  	    v = i;
551  	  }
552  	}
553  	
554  	
555  	// LBA
556  	//
557  	// first 1-3 bits = how many low zero bits
558  	//     *0 = 12 (common 4 K alignment case)
559  	//    *01 = 16
560  	//   *011 = 20
561  	//   *111 = byte
562  	// then 28-30 bits of data
563  	// then last bit = another byte follows
564  	// high bit of each subsequent byte = another byte follows
565  	inline void denc_lba(uint64_t v, size_t& p) {
566  	  p += sizeof(v) + 2;
567  	}
568  	
569  	template<class It>
570  	inline std::enable_if_t<!is_const_iterator_v<It>>
571  	denc_lba(uint64_t v, It& p) {
572  	  int low_zero_nibbles = v ? (int)(ctz(v) / 4) : 0;
573  	  int pos;
574  	  uint32_t word;
575  	  int t = low_zero_nibbles - 3;
576  	  if (t < 0) {
577  	    pos = 3;
578  	    word = 0x7;
579  	  } else if (t < 3) {
580  	    v >>= (low_zero_nibbles * 4);
581  	    pos = t + 1;
582  	    word = (1 << t) - 1;
583  	  } else {
584  	    v >>= 20;
585  	    pos = 3;
586  	    word = 0x3;
587  	  }
588  	  word |= (v << pos) & 0x7fffffff;
589  	  v >>= 31 - pos;
590  	  if (!v) {
591  	    *(ceph_le32*)p.get_pos_add(sizeof(uint32_t)) = word;
592  	    return;
593  	  }
594  	  word |= 0x80000000;
595  	  *(ceph_le32*)p.get_pos_add(sizeof(uint32_t)) = word;
596  	  uint8_t byte = v & 0x7f;
597  	  v >>= 7;
598  	  while (v) {
599  	    byte |= 0x80;
600  	    *(__u8*)p.get_pos_add(1) = byte;
601  	    byte = (v & 0x7f);
602  	    v >>= 7;
603  	  }
604  	  *(__u8*)p.get_pos_add(1) = byte;
605  	}
606  	
607  	template<class It>
608  	inline std::enable_if_t<is_const_iterator_v<It>>
609  	denc_lba(uint64_t& v, It& p) {
610  	  uint32_t word = *(ceph_le32*)p.get_pos_add(sizeof(uint32_t));
611  	  int shift;
612  	  switch (word & 7) {
613  	  case 0:
614  	  case 2:
615  	  case 4:
616  	  case 6:
617  	    v = (uint64_t)(word & 0x7ffffffe) << (12 - 1);
618  	    shift = 12 + 30;
619  	    break;
620  	  case 1:
621  	  case 5:
622  	    v = (uint64_t)(word & 0x7ffffffc) << (16 - 2);
623  	    shift = 16 + 29;
624  	    break;
625  	  case 3:
626  	    v = (uint64_t)(word & 0x7ffffff8) << (20 - 3);
627  	    shift = 20 + 28;
628  	    break;
629  	  case 7:
630  	    v = (uint64_t)(word & 0x7ffffff8) >> 3;
631  	    shift = 28;
632  	  }
633  	  uint8_t byte = word >> 24;
634  	  while (byte & 0x80) {
635  	    byte = *(__u8*)p.get_pos_add(1);
636  	    v |= (uint64_t)(byte & 0x7f) << shift;
637  	    shift += 7;
638  	  }
639  	}
640  	
641  	
642  	// ---------------------------------------------------------------------
643  	// denc top-level methods that call into denc_traits<T> methods
644  	
645  	template<typename T, typename traits=denc_traits<T>>
646  	inline std::enable_if_t<traits::supported> denc(
647  	  const T& o,
648  	  size_t& p,
649  	  uint64_t f=0)
650  	{
651  	  if constexpr (traits::featured) {
652  	    traits::bound_encode(o, p, f);
653  	  } else {
654  	    traits::bound_encode(o, p);
655  	  }
656  	}
657  	
658  	template<typename T, class It, typename traits=denc_traits<T>>
659  	inline std::enable_if_t<traits::supported && !is_const_iterator_v<It>>
660  	denc(const T& o,
661  	     It& p,
662  	     uint64_t features=0)
663  	{
664  	  if constexpr (traits::featured) {
665  	    traits::encode(o, p, features);
666  	  } else {
667  	    traits::encode(o, p);
668  	  }
669  	}
670  	
671  	template<typename T, class It, typename traits=denc_traits<T>>
672  	inline std::enable_if_t<traits::supported && is_const_iterator_v<It>>
673  	denc(T& o,
674  	     It& p,
675  	     uint64_t features=0)
676  	{
677  	  if constexpr (traits::featured) {
678  	    traits::decode(o, p, features);
679  	  } else {
680  	    traits::decode(o, p);
681  	  }
682  	}
683  	
684  	namespace _denc {
685  	template<typename T, typename = void>
686  	struct has_legacy_denc : std::false_type {};
687  	template<typename T>
688  	struct has_legacy_denc<T, decltype(std::declval<T&>()
689  					   .decode(std::declval<
690  						   ceph::buffer::list::const_iterator&>()))>
691  	  : std::true_type {
692  	  static void decode(T& v, ceph::buffer::list::const_iterator& p) {
693  	    v.decode(p);
694  	  }
695  	};
696  	template<typename T>
697  	struct has_legacy_denc<T,
698  			       std::enable_if_t<
699  				 !denc_traits<T>::need_contiguous>> : std::true_type {
700  	  static void decode(T& v, ceph::buffer::list::const_iterator& p) {
701  	    denc_traits<T>::decode(v, p);
702  	  }
703  	};
704  	}
705  	
706  	template<typename T,
707  		 typename traits=denc_traits<T>,
708  		 typename has_legacy_denc=_denc::has_legacy_denc<T>>
709  	inline std::enable_if_t<traits::supported &&
710  				has_legacy_denc::value> denc(
711  	  T& o,
712  	  ceph::buffer::list::const_iterator& p)
713  	{
714  	  has_legacy_denc::decode(o, p);
715  	}
716  	
717  	// ---------------------------------------------------------------------
718  	// base types and containers
719  	
720  	//
721  	// std::string
722  	//
723  	template<typename A>
724  	struct denc_traits<std::basic_string<char,std::char_traits<char>,A>> {
725  	private:
726  	  using value_type = std::basic_string<char,std::char_traits<char>,A>;
727  	
728  	public:
729  	  static constexpr bool supported = true;
730  	  static constexpr bool featured = false;
731  	  static constexpr bool bounded = false;
732  	  static constexpr bool need_contiguous = false;
733  	
734  	  static void bound_encode(const value_type& s, size_t& p, uint64_t f=0) {
735  	    p += sizeof(uint32_t) + s.size();
736  	  }
737  	  template<class It>
738  	  static void encode(const value_type& s,
739  			     It& p,
740  	                     uint64_t f=0) {
741  	    denc((uint32_t)s.size(), p);
742  	    memcpy(p.get_pos_add(s.size()), s.data(), s.size());
743  	  }
744  	  template<class It>
745  	  static void decode(value_type& s,
746  			     It& p,
747  			     uint64_t f=0) {
748  	    uint32_t len;
749  	    denc(len, p);
750  	    decode_nohead(len, s, p);
751  	  }
752  	  static void decode(value_type& s, ceph::buffer::list::const_iterator& p)
753  	  {
754  	    uint32_t len;
755  	    denc(len, p);
756  	    decode_nohead(len, s, p);
757  	  }
758  	  template<class It>
759  	  static void decode_nohead(size_t len, value_type& s, It& p) {
760  	    s.clear();
761  	    if (len) {
762  	      s.append(p.get_pos_add(len), len);
763  	    }
764  	  }
765  	  static void decode_nohead(size_t len, value_type& s,
766  	                            ceph::buffer::list::const_iterator& p) {
767  	    if (len) {
768  	      if constexpr (std::is_same_v<value_type, std::string>) {
769  	        s.clear();
770  	        p.copy(len, s);
771  	      } else {
772  	        s.resize(len);
773  	        p.copy(len, s.data());
774  	      }
775  	    } else {
776  	      s.clear();
777  	    }
778  	  }
779  	  template<class It>
780  	  static std::enable_if_t<!is_const_iterator_v<It>>
781  	  encode_nohead(const value_type& s, It& p) {
782  	    auto len = s.length();
783  	    maybe_inline_memcpy(p.get_pos_add(len), s.data(), len, 16);
784  	  }
785  	};
786  	
787  	//
788  	// ceph::buffer::ptr
789  	//
790  	template<>
791  	struct denc_traits<ceph::buffer::ptr> {
792  	  static constexpr bool supported = true;
793  	  static constexpr bool featured = false;
794  	  static constexpr bool bounded = false;
795  	  static constexpr bool need_contiguous = false;
796  	  static void bound_encode(const ceph::buffer::ptr& v, size_t& p, uint64_t f=0) {
797  	    p += sizeof(uint32_t) + v.length();
798  	  }
799  	  template <class It>
800  	  static std::enable_if_t<!is_const_iterator_v<It>>
801  	  encode(const ceph::buffer::ptr& v, It& p, uint64_t f=0) {
802  	    denc((uint32_t)v.length(), p);
803  	    p.append(v);
804  	  }
805  	  template <class It>
806  	  static std::enable_if_t<is_const_iterator_v<It>>
807  	  decode(ceph::buffer::ptr& v, It& p, uint64_t f=0) {
808  	    uint32_t len;
809  	    denc(len, p);
810  	    v = p.get_ptr(len);
811  	  }
812  	  static void decode(ceph::buffer::ptr& v, ceph::buffer::list::const_iterator& p) {
813  	    uint32_t len;
814  	    denc(len, p);
815  	    ceph::buffer::list s;
816  	    p.copy(len, s);
817  	    if (len) {
818  	      if (s.get_num_buffers() == 1)
819  		v = s.front();
820  	      else
821  		v = ceph::buffer::copy(s.c_str(), s.length());
822  	    }
823  	  }
824  	};
825  	
826  	//
827  	// ceph::buffer::list
828  	//
829  	template<>
830  	struct denc_traits<ceph::buffer::list> {
831  	  static constexpr bool supported = true;
832  	  static constexpr bool featured = false;
833  	  static constexpr bool bounded = false;
834  	  static constexpr bool need_contiguous = false;
835  	  static void bound_encode(const ceph::buffer::list& v, size_t& p, uint64_t f=0) {
836  	    p += sizeof(uint32_t) + v.length();
837  	  }
838  	  static void encode(const ceph::buffer::list& v, ceph::buffer::list::contiguous_appender& p,
839  		      uint64_t f=0) {
840  	    denc((uint32_t)v.length(), p);
841  	    p.append(v);
842  	  }
843  	  static void decode(ceph::buffer::list& v, ceph::buffer::ptr::const_iterator& p, uint64_t f=0) {
844  	    uint32_t len;
845  	    denc(len, p);
846  	    v.clear();
847  	    v.push_back(p.get_ptr(len));
848  	  }
849  	  static void decode(ceph::buffer::list& v, ceph::buffer::list::const_iterator& p) {
850  	    uint32_t len;
851  	    denc(len, p);
852  	    v.clear();
853  	    p.copy(len, v);
854  	  }
855  	  static void encode_nohead(const ceph::buffer::list& v,
856  				    ceph::buffer::list::contiguous_appender& p) {
857  	    p.append(v);
858  	  }
859  	  static void decode_nohead(size_t len, ceph::buffer::list& v,
860  				    ceph::buffer::ptr::const_iterator& p) {
861  	    v.clear();
862  	    if (len) {
863  	      v.append(p.get_ptr(len));
864  	    }
865  	  }
866  	  static void decode_nohead(size_t len, ceph::buffer::list& v,
867  				    ceph::buffer::list::const_iterator& p) {
868  	    v.clear();
869  	    p.copy(len, v);
870  	  }
871  	};
872  	
873  	//
874  	// std::pair<A, B>
875  	//
876  	template<typename A, typename B>
877  	struct denc_traits<
878  	  std::pair<A, B>,
879  	  std::enable_if_t<denc_supported<A> && denc_supported<B>>> {
880  	  typedef denc_traits<A> a_traits;
881  	  typedef denc_traits<B> b_traits;
882  	
883  	  static constexpr bool supported = true;
884  	  static constexpr bool featured = a_traits::featured || b_traits::featured ;
885  	  static constexpr bool bounded = a_traits::bounded && b_traits::bounded;
886  	  static constexpr bool need_contiguous = (a_traits::need_contiguous ||
887  						   b_traits::need_contiguous);
888  	
889  	  static void bound_encode(const std::pair<A,B>& v, size_t& p, uint64_t f = 0) {
890  	    if constexpr (featured) {
891  	      denc(v.first, p, f);
892  	      denc(v.second, p, f);
893  	    } else {
894  	      denc(v.first, p);
895  	      denc(v.second, p);
896  	    }
897  	  }
898  	
899  	  static void encode(const std::pair<A,B>& v, ceph::buffer::list::contiguous_appender& p,
900  			     uint64_t f = 0) {
901  	    if constexpr (featured) {
902  	      denc(v.first, p, f);
903  	      denc(v.second, p, f);
904  	    } else {
905  	      denc(v.first, p);
906  	      denc(v.second, p);
907  	    }
908  	  }
909  	
910  	  static void decode(std::pair<A,B>& v, ceph::buffer::ptr::const_iterator& p, uint64_t f=0) {
911  	    denc(v.first, p, f);
912  	    denc(v.second, p, f);
913  	  }
914  	  template<typename AA=A>
915  	  static std::enable_if_t<!!sizeof(AA) && !need_contiguous>
916  	  decode(std::pair<A,B>& v, ceph::buffer::list::const_iterator& p,
917  		 uint64_t f = 0) {
918  	    denc(v.first, p);
919  	    denc(v.second, p);
920  	  }
921  	};
922  	
923  	namespace _denc {
924  	  template<template<class...> class C, typename Details, typename ...Ts>
925  	  struct container_base {
926  	  private:
927  	    using container = C<Ts...>;
928  	    using T = typename Details::T;
929  	
930  	  public:
931  	    using traits = denc_traits<T>;
932  	
933  	    static constexpr bool supported = true;
934  	    static constexpr bool featured = traits::featured;
935  	    static constexpr bool bounded = false;
936  	    static constexpr bool need_contiguous = traits::need_contiguous;
937  	
938  	    template<typename U=T>
939  	    static void bound_encode(const container& s, size_t& p, uint64_t f = 0) {
940  	      p += sizeof(uint32_t);
941  	      if constexpr (traits::bounded) {
942  	#if _GLIBCXX_USE_CXX11_ABI
943  	        // intensionally not calling container's empty() method to not prohibit
944  	        // compiler from optimizing the check if it and the ::size() operate on
945  	        // different memory (observed when std::list::empty() works on pointers,
946  	        // not the size field).
947  	        if (const auto elem_num = s.size(); elem_num > 0) {
948  	#else
949  	        if (!s.empty()) {
950  		  const auto elem_num = s.size();
951  	#endif
952  	          // STL containers use weird element types like std::pair<const K, V>;
953  	          // cast to something we have denc_traits for.
954  	          size_t elem_size = 0;
955  	          if constexpr (traits::featured) {
956  	            denc(static_cast<const T&>(*s.begin()), elem_size, f);
957  	          } else {
958  	            denc(static_cast<const T&>(*s.begin()), elem_size);
959  	          }
960  	          p += elem_size * elem_num;
961  	        }
962  	      } else {
963  	        for (const T& e : s) {
964  	          if constexpr (traits::featured) {
965  	            denc(e, p, f);
966  	          } else {
967  	            denc(e, p);
968  	          }
969  	        }
970  	      }
971  	    }
972  	
973  	    template<typename U=T>
974  	    static void encode(const container& s,
975  			       ceph::buffer::list::contiguous_appender& p,
976  			       uint64_t f = 0) {
977  	      denc((uint32_t)s.size(), p);
978  	      if constexpr (traits::featured) {
979  	        encode_nohead(s, p, f);
980  	      } else {
981  	        encode_nohead(s, p);
982  	      }
983  	    }
984  	    static void decode(container& s, ceph::buffer::ptr::const_iterator& p,
985  			       uint64_t f = 0) {
986  	      uint32_t num;
987  	      denc(num, p);
988  	      decode_nohead(num, s, p, f);
989  	    }
990  	    template<typename U=T>
991  	    static std::enable_if_t<!!sizeof(U) && !need_contiguous>
992  	    decode(container& s, ceph::buffer::list::const_iterator& p) {
993  	      uint32_t num;
994  	      denc(num, p);
995  	      decode_nohead(num, s, p);
996  	    }
997  	
998  	    // nohead
999  	    static void encode_nohead(const container& s, ceph::buffer::list::contiguous_appender& p,
1000 				      uint64_t f = 0) {
1001 	      for (const T& e : s) {
1002 	        if constexpr (traits::featured) {
1003 	          denc(e, p, f);
1004 	        } else {
1005 	          denc(e, p);
1006 	        }
1007 	      }
1008 	    }
1009 	    static void decode_nohead(size_t num, container& s,
1010 				      ceph::buffer::ptr::const_iterator& p,
1011 				      uint64_t f=0) {
1012 	      s.clear();
1013 	      Details::reserve(s, num);
1014 	      while (num--) {
1015 		T t;
1016 		denc(t, p, f);
1017 		Details::insert(s, std::move(t));
1018 	      }
1019 	    }
1020 	    template<typename U=T>
1021 	    static std::enable_if_t<!!sizeof(U) && !need_contiguous>
1022 	    decode_nohead(size_t num, container& s,
1023 			  ceph::buffer::list::const_iterator& p) {
1024 	      s.clear();
1025 	      Details::reserve(s, num);
1026 	      while (num--) {
1027 		T t;
1028 		denc(t, p);
1029 		Details::insert(s, std::move(t));
1030 	      }
1031 	    }
1032 	  };
1033 	
1034 	  template<typename T>
1035 	  class container_has_reserve {
1036 	    template<typename U, U> struct SFINAE_match;
1037 	    template<typename U>
1038 	    static std::true_type test(SFINAE_match<T(*)(typename T::size_type),
1039 				       &U::reserve>*);
1040 	
1041 	    template<typename U>
1042 	    static std::false_type test(...);
1043 	
1044 	  public:
1045 	    static constexpr bool value = decltype(
1046 	      test<denc_traits<T>>(0))::value;
1047 	  };
1048 	  template<typename T>
1049 	  inline constexpr bool container_has_reserve_v =
1050 	    container_has_reserve<T>::value;
1051 	
1052 	
1053 	  template<typename Container>
1054 	  struct container_details_base {
1055 	    using T = typename Container::value_type;
1056 	    static void reserve(Container& c, size_t s) {
(1) Event stray_semicolon: An "if" statement with no "then" or "else" is suspicious.
(2) Event remediation: Is the ";" after "if (false)" extraneous, or is the "if" itself unnecessary?
1057 	      if constexpr (container_has_reserve_v<Container>) {
1058 	        c.reserve(s);
1059 	      }
1060 	    }
1061 	  };
1062 	
1063 	  template<typename Container>
1064 	  struct pushback_details : public container_details_base<Container> {
1065 	    template<typename ...Args>
1066 	    static void insert(Container& c, Args&& ...args) {
1067 	      c.emplace_back(std::forward<Args>(args)...);
1068 	    }
1069 	  };
1070 	}
1071 	
1072 	template<typename T, typename ...Ts>
1073 	struct denc_traits<
1074 	  std::list<T, Ts...>,
1075 	  typename std::enable_if_t<denc_traits<T>::supported>>
1076 	  : public _denc::container_base<std::list,
1077 					 _denc::pushback_details<std::list<T, Ts...>>,
1078 					 T, Ts...> {};
1079 	
1080 	template<typename T, typename ...Ts>
1081 	struct denc_traits<
1082 	  std::vector<T, Ts...>,
1083 	  typename std::enable_if_t<denc_traits<T>::supported>>
1084 	  : public _denc::container_base<std::vector,
1085 					 _denc::pushback_details<std::vector<T, Ts...>>,
1086 					 T, Ts...> {};
1087 	
1088 	namespace _denc {
1089 	  template<typename Container>
1090 	  struct setlike_details : public container_details_base<Container> {
1091 	    using T = typename Container::value_type;
1092 	    template<typename ...Args>
1093 	    static void insert(Container& c, Args&& ...args) {
1094 	      c.emplace_hint(c.cend(), std::forward<Args>(args)...);
1095 	    }
1096 	  };
1097 	}
1098 	
1099 	template<typename T, typename ...Ts>
1100 	struct denc_traits<
1101 	  std::set<T, Ts...>,
1102 	  std::enable_if_t<denc_traits<T>::supported>>
1103 	  : public _denc::container_base<std::set,
1104 					 _denc::setlike_details<std::set<T, Ts...>>,
1105 					 T, Ts...> {};
1106 	
1107 	template<typename T, typename ...Ts>
1108 	struct denc_traits<
1109 	  boost::container::flat_set<T, Ts...>,
1110 	  std::enable_if_t<denc_traits<T>::supported>>
1111 	  : public _denc::container_base<
1112 	  boost::container::flat_set,
1113 	  _denc::setlike_details<boost::container::flat_set<T, Ts...>>,
1114 	  T, Ts...> {};
1115 	
1116 	namespace _denc {
1117 	  template<typename Container>
1118 	  struct maplike_details : public container_details_base<Container> {
1119 	    using T = std::pair<typename Container::key_type,
1120 				typename Container::mapped_type>;
1121 	    template<typename ...Args>
1122 	    static void insert(Container& c, Args&& ...args) {
1123 	      c.emplace_hint(c.cend(), std::forward<Args>(args)...);
1124 	    }
1125 	  };
1126 	}
1127 	
1128 	template<typename A, typename B, typename ...Ts>
1129 	struct denc_traits<
1130 	  std::map<A, B, Ts...>,
1131 	  std::enable_if_t<denc_traits<A>::supported &&
1132 			   denc_traits<B>::supported>>
1133 	  : public _denc::container_base<std::map,
1134 					 _denc::maplike_details<std::map<A, B, Ts...>>,
1135 					 A, B, Ts...> {};
1136 	
1137 	template<typename A, typename B, typename ...Ts>
1138 	struct denc_traits<
1139 	  boost::container::flat_map<A, B, Ts...>,
1140 	  std::enable_if_t<denc_traits<A>::supported &&
1141 			   denc_traits<B>::supported>>
1142 	  : public _denc::container_base<
1143 	  boost::container::flat_map,
1144 	  _denc::maplike_details<boost::container::flat_map<
1145 				   A, B, Ts...>>,
1146 	  A, B, Ts...> {};
1147 	
1148 	template<typename T, size_t N>
1149 	struct denc_traits<
1150 	  std::array<T, N>,
1151 	  std::enable_if_t<denc_traits<T>::supported>> {
1152 	private:
1153 	  using container = std::array<T, N>;
1154 	public:
1155 	  using traits = denc_traits<T>;
1156 	
1157 	  static constexpr bool supported = true;
1158 	  static constexpr bool featured = traits::featured;
1159 	  static constexpr bool bounded = traits::bounded;
1160 	  static constexpr bool need_contiguous = traits::need_contiguous;
1161 	
1162 	  static void bound_encode(const container& s, size_t& p, uint64_t f = 0) {
1163 	    if constexpr (traits::bounded) {
1164 	      if constexpr (traits::featured) {
1165 	        if (!s.empty()) {
1166 	          size_t elem_size = 0;
1167 	          denc(*s.begin(), elem_size, f);
1168 	          p += elem_size * s.size();
1169 	        }
1170 	      } else {
1171 	        size_t elem_size = 0;
1172 	        denc(*s.begin(), elem_size);
1173 	        p += elem_size * N;
1174 	      }
1175 	    } else {
1176 	      for (const auto& e : s) {
1177 	        if constexpr (traits::featured) {
1178 	          denc(e, p, f);
1179 	        } else {
1180 	          denc(e, p);
1181 	        }
1182 	      }
1183 	    }
1184 	  }
1185 	
1186 	  static void encode(const container& s, ceph::buffer::list::contiguous_appender& p,
1187 			     uint64_t f = 0) {
1188 	    for (const auto& e : s) {
1189 	      if constexpr (traits::featured) {
1190 	        denc(e, p, f);
1191 	      } else {
1192 	        denc(e, p);
1193 	      }
1194 	    }
1195 	  }
1196 	  static void decode(container& s, ceph::buffer::ptr::const_iterator& p,
1197 			     uint64_t f = 0) {
1198 	    for (auto& e : s)
1199 	      denc(e, p, f);
1200 	  }
1201 	  template<typename U=T>
1202 	  static std::enable_if_t<!!sizeof(U) &&
1203 				  !need_contiguous>
1204 	  decode(container& s, ceph::buffer::list::const_iterator& p) {
1205 	    for (auto& e : s) {
1206 	      denc(e, p);
1207 	    }
1208 	  }
1209 	};
1210 	
1211 	template<typename... Ts>
1212 	struct denc_traits<
1213 	  std::tuple<Ts...>,
1214 	  std::enable_if_t<(denc_traits<Ts>::supported && ...)>> {
1215 	
1216 	private:
1217 	  static_assert(sizeof...(Ts) > 0,
1218 			"Zero-length tuples are not supported.");
1219 	  using container = std::tuple<Ts...>;
1220 	
1221 	public:
1222 	
1223 	  static constexpr bool supported = true;
1224 	  static constexpr bool featured = (denc_traits<Ts>::featured || ...);
1225 	  static constexpr bool bounded = (denc_traits<Ts>::bounded && ...);
1226 	  static constexpr bool need_contiguous =
1227 	      (denc_traits<Ts>::need_contiguous || ...);
1228 	
1229 	  template<typename U = container>
1230 	  static std::enable_if_t<denc_traits<U>::featured>
1231 	  bound_encode(const container& s, size_t& p, uint64_t f) {
1232 	    ceph::for_each(s, [&p, f] (const auto& e) {
1233 		if constexpr (denc_traits<std::decay_t<decltype(e)>>::featured) {
1234 		  denc(e, p, f);
1235 		} else {
1236 		  denc(e, p);
1237 		}
1238 	      });
1239 	  }
1240 	  template<typename U = container>
1241 	  static std::enable_if_t<!denc_traits<U>::featured>
1242 	  bound_encode(const container& s, size_t& p) {
1243 	    ceph::for_each(s, [&p] (const auto& e) {
1244 		denc(e, p);
1245 	      });
1246 	  }
1247 	
1248 	  template<typename U = container>
1249 	  static std::enable_if_t<denc_traits<U>::featured>
1250 	  encode(const container& s, ceph::buffer::list::contiguous_appender& p,
1251 		 uint64_t f) {
1252 	    ceph::for_each(s, [&p, f] (const auto& e) {
1253 		if constexpr (denc_traits<std::decay_t<decltype(e)>>::featured) {
1254 		  denc(e, p, f);
1255 		} else {
1256 		  denc(e, p);
1257 		}
1258 	      });
1259 	  }
1260 	  template<typename U = container>
1261 	  static std::enable_if_t<!denc_traits<U>::featured>
1262 	  encode(const container& s, ceph::buffer::list::contiguous_appender& p) {
1263 	    ceph::for_each(s, [&p] (const auto& e) {
1264 		denc(e, p);
1265 	      });
1266 	  }
1267 	
1268 	  static void decode(container& s, ceph::buffer::ptr::const_iterator& p,
1269 			     uint64_t f = 0) {
1270 	    ceph::for_each(s, [&p] (auto& e) {
1271 		denc(e, p);
1272 	      });
1273 	  }
1274 	
1275 	  template<typename U = container>
1276 	  static std::enable_if_t<!denc_traits<U>::need_contiguous>
1277 	  decode(container& s, ceph::buffer::list::const_iterator& p, uint64_t f = 0) {
1278 	    ceph::for_each(s, [&p] (auto& e) {
1279 		denc(e, p);
1280 	      });
1281 	  }
1282 	};
1283 	
1284 	//
1285 	// boost::optional<T>
1286 	//
1287 	template<typename T>
1288 	struct denc_traits<
1289 	  boost::optional<T>,
1290 	  std::enable_if_t<denc_traits<T>::supported>> {
1291 	  using traits = denc_traits<T>;
1292 	
1293 	  static constexpr bool supported = true;
1294 	  static constexpr bool featured = traits::featured;
1295 	  static constexpr bool bounded = false;
1296 	  static constexpr bool need_contiguous = traits::need_contiguous;
1297 	
1298 	  static void bound_encode(const boost::optional<T>& v, size_t& p,
1299 				   uint64_t f = 0) {
1300 	    p += sizeof(bool);
1301 	    if (v) {
1302 	      if constexpr (featured) {
1303 	        denc(*v, p, f);
1304 	      } else {
1305 	        denc(*v, p);
1306 	      }
1307 	    }
1308 	  }
1309 	
1310 	  static void encode(const boost::optional<T>& v,
1311 			     ceph::buffer::list::contiguous_appender& p,
1312 			     uint64_t f = 0) {
1313 	    denc((bool)v, p);
1314 	    if (v) {
1315 	      if constexpr (featured) {
1316 	        denc(*v, p, f);
1317 	      } else {
1318 	        denc(*v, p);
1319 	      }
1320 	    }
1321 	  }
1322 	
1323 	  static void decode(boost::optional<T>& v, ceph::buffer::ptr::const_iterator& p,
1324 			     uint64_t f = 0) {
1325 	    bool x;
1326 	    denc(x, p, f);
1327 	    if (x) {
1328 	      v = T{};
1329 	      denc(*v, p, f);
1330 	    } else {
1331 	      v = boost::none;
1332 	    }
1333 	  }
1334 	
1335 	  template<typename U = T>
1336 	  static std::enable_if_t<!!sizeof(U) && !need_contiguous>
1337 	  decode(boost::optional<T>& v, ceph::buffer::list::const_iterator& p) {
1338 	    bool x;
1339 	    denc(x, p);
1340 	    if (x) {
1341 	      v = T{};
1342 	      denc(*v, p);
1343 	    } else {
1344 	      v = boost::none;
1345 	    }
1346 	  }
1347 	
1348 	  template<typename U = T>
1349 	  static void encode_nohead(const boost::optional<T>& v,
1350 				    ceph::buffer::list::contiguous_appender& p,
1351 				    uint64_t f = 0) {
1352 	    if (v) {
1353 	      if constexpr (featured) {
1354 	        denc(*v, p, f);
1355 	      } else {
1356 	        denc(*v, p);
1357 	      }
1358 	    }
1359 	  }
1360 	
1361 	  static void decode_nohead(bool num, boost::optional<T>& v,
1362 				    ceph::buffer::ptr::const_iterator& p, uint64_t f = 0) {
1363 	    if (num) {
1364 	      v = T();
1365 	      denc(*v, p, f);
1366 	    } else {
1367 	      v = boost::none;
1368 	    }
1369 	  }
1370 	};
1371 	
1372 	template<>
1373 	struct denc_traits<boost::none_t> {
1374 	  static constexpr bool supported = true;
1375 	  static constexpr bool featured = false;
1376 	  static constexpr bool bounded = true;
1377 	  static constexpr bool need_contiguous = false;
1378 	
1379 	  static void bound_encode(const boost::none_t& v, size_t& p) {
1380 	    p += sizeof(bool);
1381 	  }
1382 	
1383 	  static void encode(const boost::none_t& v,
1384 			     ceph::buffer::list::contiguous_appender& p) {
1385 	    denc(false, p);
1386 	  }
1387 	};
1388 	
1389 	//
1390 	// std::optional<T>
1391 	//
1392 	template<typename T>
1393 	struct denc_traits<
1394 	  std::optional<T>,
1395 	  std::enable_if_t<denc_traits<T>::supported>> {
1396 	  using traits = denc_traits<T>;
1397 	
1398 	  static constexpr bool supported = true;
1399 	  static constexpr bool featured = traits::featured;
1400 	  static constexpr bool bounded = false;
1401 	  static constexpr bool need_contiguous = traits::need_contiguous;
1402 	
1403 	  static void bound_encode(const std::optional<T>& v, size_t& p,
1404 				   uint64_t f = 0) {
1405 	    p += sizeof(bool);
1406 	    if (v) {
1407 	      if constexpr (featured) {
1408 	        denc(*v, p, f);
1409 	      } else {
1410 	        denc(*v, p);
1411 	      }
1412 	    }
1413 	  }
1414 	
1415 	  static void encode(const std::optional<T>& v,
1416 			     ceph::buffer::list::contiguous_appender& p,
1417 			     uint64_t f = 0) {
1418 	    denc((bool)v, p);
1419 	    if (v) {
1420 	      if constexpr (featured) {
1421 	        denc(*v, p, f);
1422 	      } else {
1423 	        denc(*v, p);
1424 	      }
1425 	    }
1426 	  }
1427 	
1428 	  static void decode(std::optional<T>& v, ceph::buffer::ptr::const_iterator& p,
1429 			     uint64_t f = 0) {
1430 	    bool x;
1431 	    denc(x, p, f);
1432 	    if (x) {
1433 	      v = T{};
1434 	      denc(*v, p, f);
1435 	    } else {
1436 	      v = std::nullopt;
1437 	    }
1438 	  }
1439 	
1440 	  template<typename U = T>
1441 	  static std::enable_if_t<!!sizeof(U) && !need_contiguous>
1442 	  decode(std::optional<T>& v, ceph::buffer::list::const_iterator& p) {
1443 	    bool x;
1444 	    denc(x, p);
1445 	    if (x) {
1446 	      v = T{};
1447 	      denc(*v, p);
1448 	    } else {
1449 	      v = std::nullopt;
1450 	    }
1451 	  }
1452 	
1453 	  static void encode_nohead(const std::optional<T>& v,
1454 				    ceph::buffer::list::contiguous_appender& p,
1455 				    uint64_t f = 0) {
1456 	    if (v) {
1457 	      if constexpr (featured) {
1458 	        denc(*v, p, f);
1459 	      } else {
1460 	        denc(*v, p);
1461 	      }
1462 	    }
1463 	  }
1464 	
1465 	  static void decode_nohead(bool num, std::optional<T>& v,
1466 				    ceph::buffer::ptr::const_iterator& p, uint64_t f = 0) {
1467 	    if (num) {
1468 	      v = T();
1469 	      denc(*v, p, f);
1470 	    } else {
1471 	      v = std::nullopt;
1472 	    }
1473 	  }
1474 	};
1475 	
1476 	template<>
1477 	struct denc_traits<std::nullopt_t> {
1478 	  static constexpr bool supported = true;
1479 	  static constexpr bool featured = false;
1480 	  static constexpr bool bounded = true;
1481 	  static constexpr bool need_contiguous = false;
1482 	
1483 	  static void bound_encode(const std::nullopt_t& v, size_t& p) {
1484 	    p += sizeof(bool);
1485 	  }
1486 	
1487 	  static void encode(const std::nullopt_t& v,
1488 			     ceph::buffer::list::contiguous_appender& p) {
1489 	    denc(false, p);
1490 	  }
1491 	};
1492 	
1493 	// ----------------------------------------------------------------------
1494 	// class helpers
1495 	
1496 	// Write denc_traits<> for a class that defines bound_encode/encode/decode
1497 	// methods.
1498 	
1499 	#define WRITE_CLASS_DENC(T) _DECLARE_CLASS_DENC(T, false)
1500 	#define WRITE_CLASS_DENC_BOUNDED(T) _DECLARE_CLASS_DENC(T, true)
1501 	#define _DECLARE_CLASS_DENC(T, b)					\
1502 	  template<> struct denc_traits<T> {					\
1503 	    static constexpr bool supported = true;				\
1504 	    static constexpr bool featured = false;				\
1505 	    static constexpr bool bounded = b;					\
1506 	    static constexpr bool need_contiguous = !_denc::has_legacy_denc<T>::value;\
1507 	    static void bound_encode(const T& v, size_t& p, uint64_t f=0) {	\
1508 	      v.bound_encode(p);						\
1509 	    }									\
1510 	    static void encode(const T& v, ::ceph::buffer::list::contiguous_appender& p, \
1511 			       uint64_t f=0) {					\
1512 	      v.encode(p);							\
1513 	    }									\
1514 	    static void decode(T& v, ::ceph::buffer::ptr::const_iterator& p, uint64_t f=0) { \
1515 	      v.decode(p);							\
1516 	    }									\
1517 	  };
1518 	
1519 	#define WRITE_CLASS_DENC_FEATURED(T) _DECLARE_CLASS_DENC_FEATURED(T, false)
1520 	#define WRITE_CLASS_DENC_FEATURED_BOUNDED(T) _DECLARE_CLASS_DENC_FEATURED(T, true)
1521 	#define _DECLARE_CLASS_DENC_FEATURED(T, b)				\
1522 	  template<> struct denc_traits<T> {					\
1523 	    static constexpr bool supported = true;				\
1524 	    static constexpr bool featured = true;				\
1525 	    static constexpr bool bounded = b;					\
1526 	    static constexpr bool need_contiguous = !_denc::has_legacy_denc<T>::value;\
1527 	    static void bound_encode(const T& v, size_t& p, uint64_t f) {	\
1528 	      v.bound_encode(p, f);						\
1529 	    }									\
1530 	    static void encode(const T& v, ::ceph::buffer::list::contiguous_appender& p, \
1531 			       uint64_t f) {					\
1532 	      v.encode(p, f);							\
1533 	    }									\
1534 	    static void decode(T& v, ::ceph::buffer::ptr::const_iterator& p, uint64_t f=0) { \
1535 	      v.decode(p, f);							\
1536 	    }									\
1537 	  };
1538 	
1539 	
1540 	// ----------------------------------------------------------------------
1541 	// encode/decode wrappers
1542 	
1543 	// These glue the new-style denc world into old-style calls to encode
1544 	// and decode by calling into denc_traits<> methods (when present).
1545 	
1546 	namespace ceph {
1547 	template<typename T, typename traits=denc_traits<T>>
1548 	inline std::enable_if_t<traits::supported && !traits::featured> encode(
1549 	  const T& o,
1550 	  ceph::buffer::list& bl,
1551 	  uint64_t features_unused=0)
1552 	{
1553 	  size_t len = 0;
1554 	  traits::bound_encode(o, len);
1555 	  auto a = bl.get_contiguous_appender(len);
1556 	  traits::encode(o, a);
1557 	}
1558 	
1559 	template<typename T, typename traits=denc_traits<T>>
1560 	inline std::enable_if_t<traits::supported && traits::featured> encode(
1561 	  const T& o, ::ceph::buffer::list& bl,
1562 	  uint64_t features)
1563 	{
1564 	  size_t len = 0;
1565 	  traits::bound_encode(o, len, features);
1566 	  auto a = bl.get_contiguous_appender(len);
1567 	  traits::encode(o, a, features);
1568 	}
1569 	
1570 	template<typename T,
1571 		 typename traits=denc_traits<T>>
1572 	inline std::enable_if_t<traits::supported && !traits::need_contiguous> decode(
1573 	  T& o,
1574 	  ::ceph::buffer::list::const_iterator& p)
1575 	{
1576 	  if (p.end())
1577 	    throw ::ceph::buffer::end_of_buffer();
1578 	  const auto& bl = p.get_bl();
1579 	  const auto remaining = bl.length() - p.get_off();
1580 	  // it is expensive to rebuild a contigous buffer and drop it, so avoid this.
1581 	  if (!p.is_pointing_same_raw(bl.back()) && remaining > CEPH_PAGE_SIZE) {
1582 	    traits::decode(o, p);
1583 	  } else {
1584 	    // ensure we get a contigous buffer... until the end of the
1585 	    // ceph::buffer::list.  we don't really know how much we'll need here,
1586 	    // unfortunately.  hopefully it is already contiguous and we're just
1587 	    // bumping the raw ref and initializing the ptr tmp fields.
1588 	    ceph::buffer::ptr tmp;
1589 	    auto t = p;
1590 	    t.copy_shallow(remaining, tmp);
1591 	    auto cp = std::cbegin(tmp);
1592 	    traits::decode(o, cp);
1593 	    p.advance(cp.get_offset());
1594 	  }
1595 	}
1596 	
1597 	template<typename T,
1598 		 typename traits=denc_traits<T>>
1599 	inline std::enable_if_t<traits::supported && traits::need_contiguous> decode(
1600 	  T& o,
1601 	  ceph::buffer::list::const_iterator& p)
1602 	{
1603 	  if (p.end())
1604 	    throw ceph::buffer::end_of_buffer();
1605 	  // ensure we get a contigous buffer... until the end of the
1606 	  // ceph::buffer::list.  we don't really know how much we'll need here,
1607 	  // unfortunately.  hopefully it is already contiguous and we're just
1608 	  // bumping the raw ref and initializing the ptr tmp fields.
1609 	  ceph::buffer::ptr tmp;
1610 	  auto t = p;
1611 	  t.copy_shallow(p.get_bl().length() - p.get_off(), tmp);
1612 	  auto cp = std::cbegin(tmp);
1613 	  traits::decode(o, cp);
1614 	  p.advance(cp.get_offset());
1615 	}
1616 	
1617 	// nohead variants
1618 	template<typename T, typename traits=denc_traits<T>>
1619 	inline std::enable_if_t<traits::supported &&
1620 				!traits::featured> encode_nohead(
1621 	  const T& o,
1622 	  ceph::buffer::list& bl)
1623 	{
1624 	  size_t len = 0;
1625 	  traits::bound_encode(o, len);
1626 	  auto a = bl.get_contiguous_appender(len);
1627 	  traits::encode_nohead(o, a);
1628 	}
1629 	
1630 	template<typename T, typename traits=denc_traits<T>>
1631 	inline std::enable_if_t<traits::supported && !traits::featured> decode_nohead(
1632 	  size_t num,
1633 	  T& o,
1634 	  ceph::buffer::list::const_iterator& p)
1635 	{
1636 	  if (!num)
1637 	    return;
1638 	  if (p.end())
1639 	    throw ceph::buffer::end_of_buffer();
1640 	  if constexpr (traits::need_contiguous) {
1641 	    ceph::buffer::ptr tmp;
1642 	    auto t = p;
1643 	    if constexpr (denc_traits<typename T::value_type>::bounded) {
1644 	      size_t element_size = 0;
1645 	      typename T::value_type v;
1646 	      denc_traits<typename T::value_type>::bound_encode(v, element_size);
1647 	      t.copy_shallow(num * element_size, tmp);
1648 	    } else {
1649 	      t.copy_shallow(p.get_bl().length() - p.get_off(), tmp);
1650 	    }
1651 	    auto cp = std::cbegin(tmp);
1652 	    traits::decode_nohead(num, o, cp);
1653 	    p.advance(cp.get_offset());
1654 	  } else {
1655 	    traits::decode_nohead(num, o, p);
1656 	  }
1657 	}
1658 	}
1659 	
1660 	
1661 	// ----------------------------------------------------------------
1662 	// DENC
1663 	
1664 	// These are some class methods we need to do the version and length
1665 	// wrappers for DENC_{START,FINISH} for inter-version
1666 	// interoperability.
1667 	
1668 	#define DENC_HELPERS							\
1669 	  /* bound_encode */							\
1670 	  static void _denc_start(size_t& p,					\
1671 				  __u8 *struct_v,				\
1672 				  __u8 *struct_compat,				\
1673 				  char **, uint32_t *) {			\
1674 	    p += 2 + 4;								\
1675 	  }									\
1676 	  static void _denc_finish(size_t& p,					\
1677 				   __u8 *struct_v,				\
1678 				   __u8 *struct_compat,				\
1679 				   char **, uint32_t *) { }			\
1680 	  /* encode */								\
1681 	  static void _denc_start(::ceph::buffer::list::contiguous_appender& p,	\
1682 				  __u8 *struct_v,				\
1683 				  __u8 *struct_compat,				\
1684 				  char **len_pos,				\
1685 				  uint32_t *start_oob_off) {			\
1686 	    denc(*struct_v, p);							\
1687 	    denc(*struct_compat, p);						\
1688 	    *len_pos = p.get_pos_add(4);					\
1689 	    *start_oob_off = p.get_out_of_band_offset();			\
1690 	  }									\
1691 	  static void _denc_finish(::ceph::buffer::list::contiguous_appender& p, \
1692 				   __u8 *struct_v,				\
1693 				   __u8 *struct_compat,				\
1694 				   char **len_pos,				\
1695 				   uint32_t *start_oob_off) {			\
1696 	    *(ceph_le32*)*len_pos = p.get_pos() - *len_pos - sizeof(uint32_t) +	\
1697 	      p.get_out_of_band_offset() - *start_oob_off;			\
1698 	  }									\
1699 	  /* decode */								\
1700 	  static void _denc_start(::ceph::buffer::ptr::const_iterator& p,	\
1701 				  __u8 *struct_v,				\
1702 				  __u8 *struct_compat,				\
1703 				  char **start_pos,				\
1704 				  uint32_t *struct_len) {			\
1705 	    denc(*struct_v, p);							\
1706 	    denc(*struct_compat, p);						\
1707 	    denc(*struct_len, p);						\
1708 	    *start_pos = const_cast<char*>(p.get_pos());			\
1709 	  }									\
1710 	  static void _denc_finish(::ceph::buffer::ptr::const_iterator& p,	\
1711 				   __u8 *struct_v, __u8 *struct_compat,		\
1712 				   char **start_pos,				\
1713 				   uint32_t *struct_len) {			\
1714 	    const char *pos = p.get_pos();					\
1715 	    char *end = *start_pos + *struct_len;				\
1716 	    ceph_assert(pos <= end);							\
1717 	    if (pos < end) {							\
1718 	      p.advance(end - pos);						\
1719 	    }									\
1720 	  }
1721 	
1722 	// Helpers for versioning the encoding.  These correspond to the
1723 	// {ENCODE,DECODE}_{START,FINISH} macros.
1724 	
1725 	#define DENC_START(v, compat, p)					\
1726 	  __u8 struct_v = v;							\
1727 	  __u8 struct_compat = compat;						\
1728 	  char *_denc_pchar;							\
1729 	  uint32_t _denc_u32;							\
1730 	  _denc_start(p, &struct_v, &struct_compat, &_denc_pchar, &_denc_u32);	\
1731 	  do {
1732 	
1733 	#define DENC_FINISH(p)							\
1734 	  } while (false);							\
1735 	  _denc_finish(p, &struct_v, &struct_compat, &_denc_pchar, &_denc_u32);
1736 	
1737 	
1738 	// ----------------------------------------------------------------------
1739 	
1740 	// Helpers for writing a unified bound_encode/encode/decode
1741 	// implementation that won't screw up buffer size estimations.
1742 	
1743 	#define DENC(Type, v, p)						\
1744 	  DENC_HELPERS								\
1745 	  void bound_encode(size_t& p) const {					\
1746 	    _denc_friend(*this, p);						\
1747 	  }									\
1748 	  void encode(::ceph::buffer::list::contiguous_appender& p) const {	\
1749 	    DENC_DUMP_PRE(Type);						\
1750 	    _denc_friend(*this, p);						\
1751 	  }									\
1752 	  void decode(::ceph::buffer::ptr::const_iterator& p) {			\
1753 	    _denc_friend(*this, p);						\
1754 	  }									\
1755 	  template<typename T, typename P>					\
1756 	  friend std::enable_if_t<std::is_same_v<T, Type> ||			\
1757 				  std::is_same_v<T, const Type>>		\
1758 	  _denc_friend(T& v, P& p)
1759 	
1760 	#define DENC_FEATURED(Type, v, p, f)					\
1761 	  DENC_HELPERS								\
1762 	  void bound_encode(size_t& p, uint64_t f) const {			\
1763 	    _denc_friend(*this, p, f);						\
1764 	  }									\
1765 	  void encode(::ceph::buffer::list::contiguous_appender& p, uint64_t f) const { \
1766 	    DENC_DUMP_PRE(Type);						\
1767 	    _denc_friend(*this, p, f);						\
1768 	  }									\
1769 	  void decode(::ceph::buffer::ptr::const_iterator& p, uint64_t f=0) {	\
1770 	    _denc_friend(*this, p, f);						\
1771 	  }									\
1772 	  template<typename T, typename P>					\
1773 	  friend std::enable_if_t<std::is_same_v<T, Type> ||			\
1774 				  std::is_same_v<T, const Type>>			\
1775 	  _denc_friend(T& v, P& p, uint64_t f)
1776 	
1777 	#endif
1778