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