]> git.proxmox.com Git - ceph.git/blame - ceph/src/test/test_denc.cc
update sources to v12.1.1
[ceph.git] / ceph / src / test / test_denc.cc
CommitLineData
7c673cae
FG
1// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2// vim: ts=8 sw=2 smarttab
3/*
4 * Ceph distributed storage system
5 *
6 * Copyright (C) 2016 Red Hat
7 *
8 * Author: Sage Weil <sage@redhat.com>
9 *
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
14 *
15 */
16
17#include <stdio.h>
31f18b77 18#include <numeric>
7c673cae
FG
19
20#include "global/global_init.h"
21#include "common/ceph_argparse.h"
22#include "global/global_context.h"
23#include "gtest/gtest.h"
24
25#include "include/denc.h"
26
27// test helpers
28
29template<typename T>
30void test_encode_decode(T v) {
31 bufferlist bl;
32 ::encode(v, bl);
33 bufferlist::iterator p = bl.begin();
34 T out;
35 ::decode(out, p);
36 ASSERT_EQ(v, out);
37}
38
39template<typename T>
40void test_denc(T v) {
41 // estimate
42 size_t s = 0;
43 denc(v, s);
44 ASSERT_NE(s, 0u);
45
46 // encode
47 bufferlist bl;
48 {
49 auto a = bl.get_contiguous_appender(s);
50 denc(v, a);
51 }
52 ASSERT_LE(bl.length(), s);
53
54 // decode
55 bl.rebuild();
56 T out;
57 auto bpi = bl.front().begin();
58 denc(out, bpi);
59 ASSERT_EQ(v, out);
60 ASSERT_EQ(bpi.get_pos(), bl.c_str() + bl.length());
61
62 // test glue
63 test_encode_decode(v);
64}
65
66template<typename T>
67void test_encode_decode_featured(T v) {
68 bufferlist bl;
69 ::encode(v, bl, 123);
70 bufferlist::iterator p = bl.begin();
71 T out;
72 ::decode(out, p);
73 ASSERT_EQ(v, out);
74}
75
76template<typename T>
77void test_denc_featured(T v) {
78 // estimate
79 size_t s = 0;
80 denc(v, s, 0);
81 ASSERT_GT(s, 0u);
82
83 // encode
84 bufferlist bl;
85 {
86 auto a = bl.get_contiguous_appender(s);
87 denc(v, a, 1);
88 }
89 ASSERT_LE(bl.length(), s);
90
91 // decode
92 bl.rebuild();
93 T out;
94 auto bpi = bl.front().begin();
95 denc(out, bpi, 1);
96 ASSERT_EQ(v, out);
97 ASSERT_EQ(bpi.get_pos(), bl.c_str() + bl.length());
98
99 // test glue
100 test_encode_decode_featured(v);
101}
102
103
104// hooks to count bound calls
105
106struct counts_t {
107 int num_bound_encode = 0;
108 int num_encode = 0;
109 int num_decode = 0;
110 void reset() {
111 num_bound_encode = 0;
112 num_encode = 0;
113 num_decode = 0;
114 }
115} counts;
116
117struct denc_counter_t {
118 void bound_encode(size_t& p) const {
119 ++counts.num_bound_encode;
120 ++p; // denc.h does not like 0-length objects
121 }
122 void encode(buffer::list::contiguous_appender& p) const {
123 p.append("a", 1);
124 ++counts.num_encode;
125 }
126 void decode(buffer::ptr::iterator &p) {
127 p.advance(1);
128 ++counts.num_decode;
129 }
130};
131WRITE_CLASS_DENC(denc_counter_t)
132
133struct denc_counter_bounded_t {
134 void bound_encode(size_t& p) const {
135 ++counts.num_bound_encode;
136 ++p; // denc.h does not like 0-length objects
137 }
138 void encode(buffer::list::contiguous_appender& p) const {
139 p.append("a", 1);
140 ++counts.num_encode;
141 }
142 void decode(buffer::ptr::iterator &p) {
143 p.advance(1);
144 ++counts.num_decode;
145 }
146};
147WRITE_CLASS_DENC_BOUNDED(denc_counter_bounded_t)
148
149TEST(denc, denc_counter)
150{
151 denc_counter_t single, single2;
152 {
153 bufferlist bl;
154 ::encode(single, bl);
155 ::decode(single2, bl);
156 }
157 ASSERT_EQ(counts.num_bound_encode, 1);
158 ASSERT_EQ(counts.num_encode, 1);
159 ASSERT_EQ(counts.num_decode, 1);
160 counts.reset();
161}
162
163TEST(denc, simple)
164{
165 test_denc((uint8_t)4);
166 test_denc((int8_t)-5);
167 test_denc((uint16_t)6);
168 test_denc((int16_t)-7);
169 test_denc((uint32_t)8);
170 test_denc((int32_t)-9);
171 test_denc((uint64_t)10);
172 test_denc((int64_t)-11);
173}
174
175TEST(denc, string)
176{
177 string a, b("hi"), c("multi\nline\n");
178 test_denc(a);
179 test_denc(b);
180 test_denc(c);
181}
182
183struct legacy_t {
184 int32_t a = 1;
185 void encode(bufferlist& bl) const {
186 ::encode(a, bl);
187 }
188 void decode(bufferlist::iterator& p) {
189 ::decode(a, p);
190 }
191 legacy_t() {}
192 legacy_t(int32_t i) : a(i) {}
193 friend bool operator<(const legacy_t& l, const legacy_t& r) {
194 return l.a < r.a;
195 }
196 friend bool operator==(const legacy_t& l, const legacy_t& r) {
197 return l.a == r.a;
198 }
199};
200WRITE_CLASS_ENCODER(legacy_t)
201
202template<template<class> class C>
203void test_common_veclist(const char* c) {
204 {
205 cout << c << "<std::string>" << std::endl;
206 C<std::string> s;
207 s.push_back("foo");
208 s.push_back("bar");
209 s.push_back("baz");
210 counts.reset();
211 test_denc(s);
212 }
213 {
214 cout << c << "<int32_t>" << std::endl;
215 C<int32_t> s;
216 s.push_back(1);
217 s.push_back(2);
218 s.push_back(3);
219 test_denc(s);
220 }
221 {
222 cout << c << "<legacy_t>" << std::endl;
223 C<legacy_t> s;
224 s.push_back(legacy_t(1));
225 s.push_back(legacy_t(2));
226 test_encode_decode(s);
227 }
228}
229
230// We only care about specializing the type, all other template
231// parameters should have the default values. (Like first-class
232// functions, first-class templates do not bring their defaults.)
233
234template<typename T>
235using default_vector = std::vector<T>;
236
237TEST(denc, vector)
238{
239 test_common_veclist<default_vector>("std::vector");
240 {
241 counts.reset();
242 vector<denc_counter_t> v, v2;
243 v.resize(100);
244 {
245 bufferlist bl;
246 ::encode(v, bl);
247 ::decode(v2, bl);
248 }
249 ASSERT_EQ(counts.num_bound_encode, 100);
250 ASSERT_EQ(counts.num_encode, 100);
251 ASSERT_EQ(counts.num_decode, 100);
252 }
253 {
254 counts.reset();
255 vector<denc_counter_bounded_t> v, v2;
256 v.resize(100);
257 {
258 bufferlist bl;
259 ::encode(v, bl);
260 ::decode(v2, bl);
261 }
262 ASSERT_EQ(counts.num_bound_encode, 1);
263 ASSERT_EQ(counts.num_encode, 100);
264 ASSERT_EQ(counts.num_decode, 100);
265 }
266}
267
268template<typename T>
269using default_list = std::list<T>;
270
271TEST(denc, list)
272{
273 test_common_veclist<default_list>("std::list");
274 {
275 counts.reset();
276 list<denc_counter_bounded_t> l, l2;
277 for (unsigned i=0; i<100; ++i) {
278 l.emplace_back(denc_counter_bounded_t());
279 }
280 {
281 bufferlist bl;
282 ::encode(l, bl);
283 ::decode(l2, bl);
284 }
285 ASSERT_EQ(counts.num_bound_encode, 1);
286 ASSERT_EQ(counts.num_encode, 100);
287 ASSERT_EQ(counts.num_decode, 100);
288 }
289}
290
291template<template<class> class C>
292void test_setlike(const char* c) {
293 {
294 cout << c << "<std::string>" << std::endl;
295 C<std::string> s;
296 s.insert("foo");
297 s.insert("bar");
298 s.insert("baz");
299 test_denc(s);
300 }
301 {
302 cout << c << "<int32_t>" << std::endl;
303 C<int32_t> s;
304 s.insert(1);
305 s.insert(2);
306 s.insert(3);
307 test_denc(s);
308 }
309 {
310 cout << c << "<legacy_t>" << std::endl;
311 C<legacy_t> s;
312 s.insert(legacy_t(1));
313 s.insert(legacy_t(2));
314 test_encode_decode(s);
315 }
316}
317
318template<typename T>
319using default_set = std::set<T>;
320
321TEST(denc, set)
322{
323 test_setlike<default_set>("std::set");
324}
325
326template<typename T>
327using default_flat_set= boost::container::flat_set<T>;
328
329TEST(denc, flat_set)
330{
331 test_setlike<default_flat_set>("std::set");
332}
333
334struct foo_t {
335 int32_t a = 0;
336 uint64_t b = 123;
337
338 DENC(foo_t, v, p) {
339 DENC_START(1, 1, p);
340 ::denc(v.a, p);
341 ::denc(v.b, p);
342 DENC_FINISH(p);
343 }
344
345 friend bool operator==(const foo_t& l, const foo_t& r) {
346 return l.a == r.a && l.b == r.b;
347 }
348};
349WRITE_CLASS_DENC_BOUNDED(foo_t)
350
351struct foo2_t {
352 int32_t c = 0;
353 uint64_t d = 123;
354
355 DENC(foo2_t, v, p) {
356 DENC_START(1, 1, p);
357 ::denc(v.c, p);
358 ::denc(v.d, p);
359 DENC_FINISH(p);
360 }
361
362 friend bool operator==(const foo2_t& l, const foo2_t& r) {
363 return l.c == r.c && l.d == r.d;
364 }
365};
366WRITE_CLASS_DENC_BOUNDED(foo2_t)
367
368
369struct bar_t {
370 int32_t a = 0;
371 uint64_t b = 123;
372
373 DENC_FEATURED(bar_t, v, p, f) {
374 ::denc(v.a, p, f);
375 ::denc(v.b, p, f);
376 }
377
378 friend bool operator==(const bar_t& l, const bar_t& r) {
379 return l.a == r.a && l.b == r.b;
380 }
381};
382WRITE_CLASS_DENC_FEATURED_BOUNDED(bar_t)
383
384TEST(denc, foo)
385{
386 foo_t a;
387 test_denc(a);
388 bufferlist bl;
389 ::encode(a, bl);
390 bl.hexdump(cout);
391}
392
393TEST(denc, bar)
394{
395 bar_t a;
396 test_denc_featured(a);
397}
398
399
400
401TEST(denc, pair)
402{
403 pair<int32_t,std::string> p;
404 bufferlist bl;
405 {
406 auto a = bl.get_contiguous_appender(1000);
407 denc(p, a);
408 ::encode(p, bl);
409 }
410
411 pair<int32_t,legacy_t> lp;
412 ::encode(lp, bl);
413}
414
415template<template<class, class> class C>
416void test_common_maplike(const char* c) {
417 {
418 cout << c << "<std::string, foo_t>" << std::endl;
419 C<string, foo_t> s;
420 s["foo"] = foo_t();
421 s["bar"] = foo_t();
422 s["baz"] = foo_t();
423 test_denc(s);
424 }
425 {
426 cout << c << "<std::string, bar_t>" << std::endl;
427 C<string, bar_t> s;
428 s["foo"] = bar_t();
429 s["bar"] = bar_t();
430 s["baz"] = bar_t();
431 test_denc_featured(s);
432 }
433 {
434 cout << c << "<std::string, legacy_t>" << std::endl;
435 C<std::string, legacy_t> s;
436 s["foo"] = legacy_t(1);
437 s["bar"] = legacy_t(2);
438 test_encode_decode(s);
439 }
440}
441
442template<typename U, typename V>
443using default_map = std::map<U, V>;
444
445TEST(denc, map)
446{
447 test_common_maplike<default_map>("std::map");
448}
449
450template<typename U, typename V>
451using default_flat_map = boost::container::flat_map<U, V>;
452
453TEST(denc, flat_map)
454{
455 test_common_maplike<default_flat_map>("boost::container::flat_map");
456}
457
458TEST(denc, bufferptr_shallow_and_deep) {
459 // shallow encode
460 int32_t i = 1;
461 bufferptr p1("foo", 3);
462 bufferlist bl;
463 {
464 auto a = bl.get_contiguous_appender(100);
465 denc(i, a);
466 denc(p1, a);
467 denc(i, a);
468 }
469 cout << "bl is " << bl << std::endl;
470 bl.hexdump(cout);
471 ASSERT_EQ(3u, bl.get_num_buffers());
472
473 bufferlist bl2 = bl;
474 bl.rebuild();
475 bl2.rebuild();
476
477 // shallow decode
478 {
479 cout << "bl is " << bl << std::endl;
480 bl.hexdump(cout);
481 auto p = bl.front().begin();
482 bufferptr op;
483 int32_t i;
484 denc(i, p);
485 denc(op, p);
486 denc(i, p);
487 ASSERT_EQ(3u, op.length());
488 ASSERT_EQ('f', op[0]);
489 memset(bl.c_str(), 0, bl.length());
490 ASSERT_EQ(0, op[0]);
491 }
492
493 // deep decode
494 {
495 cout << "bl is " << bl2 << std::endl;
496 bl2.hexdump(cout);
497 auto p = bl2.front().begin_deep();
498 bufferptr op;
499 int32_t i;
500 denc(i, p);
501 denc(op, p);
502 denc(i, p);
503 ASSERT_EQ('f', op[0]);
504 memset(bl2.c_str(), 1, bl2.length());
505 ASSERT_EQ('f', op[0]);
506 }
507}
508
509TEST(denc, array)
510{
511 {
512 cout << "std::array<std::string, 3>" << std::endl;
513 std::array<std::string, 3> s = { "foo", "bar", "baz" };
514 counts.reset();
515 test_denc(s);
516 }
517 {
518 cout << "std::array<uint32_t, 3>" << std::endl;
519 std::array<uint32_t, 3> s = { 1UL, 2UL, 3UL };
520 test_denc(s);
521 }
522}
523
524TEST(denc, tuple)
525{
526 {
527 cout << "std::tuple<uint64_t, uint32_t>" << std::endl;
528 std::tuple<uint64_t, uint32_t> s(100ULL, 97UL);
529 counts.reset();
530 test_denc(s);
531 }
532 {
533 cout << "std::tuple<std::string, uint3_t>" << std::endl;
534 std::tuple<std::string, uint32_t> s("foo", 97);
535 test_denc(s);
536 }
537 {
538 cout << "std::tuple<std::string, std::set<uint32_t>>" << std::endl;
539 std::tuple<std::string, std::set<uint32_t>> s(
540 "bar", std::set<uint32_t>{uint32_t(1), uint32_t(2), uint32_t(3)});
541 test_denc(s);
542 }
543}
544
545TEST(denc, optional)
546{
547 {
548 cout << "boost::optional<uint64_t>" << std::endl;
549 boost::optional<uint64_t> s = 97, t = boost::none;
550 counts.reset();
551 test_denc(s);
552 test_denc(t);
553 }
554 {
555 cout << "boost::optional<std::string>" << std::endl;
556 boost::optional<std::string> s = std::string("Meow"), t = boost::none;
557 counts.reset();
558 test_denc(s);
559 test_denc(t);
560 }
561 {
562 size_t s = 0;
563 denc(boost::none, s);
564 ASSERT_NE(s, 0u);
565
566 // encode
567 bufferlist bl;
568 {
569 auto a = bl.get_contiguous_appender(s);
570 denc(boost::none, a);
571 }
572 ASSERT_LE(bl.length(), s);
573
574 bl.rebuild();
575 boost::optional<uint32_t> out = 5;
576 auto bpi = bl.front().begin();
577 denc(out, bpi);
578 ASSERT_FALSE(!!out);
579 ASSERT_EQ(bpi.get_pos(), bl.c_str() + bl.length());
580 }
581}
31f18b77
FG
582
583// unlike legacy_t, Legacy supports denc() also.
584struct Legacy {
585 static unsigned n_denc;
586 static unsigned n_decode;
587 uint8_t value = 0;
588 DENC(Legacy, v, p) {
589 n_denc++;
590 denc(v.value, p);
591 }
592 void decode(buffer::list::iterator& p) {
593 n_decode++;
594 ::decode(value, p);
595 }
596 static void reset() {
597 n_denc = n_decode = 0;
598 }
599 static bufferlist encode_n(unsigned n, const vector<unsigned>& segments);
600};
601WRITE_CLASS_DENC(Legacy)
602unsigned Legacy::n_denc = 0;
603unsigned Legacy::n_decode = 0;
604
605bufferlist Legacy::encode_n(unsigned n, const vector<unsigned>& segments) {
606 vector<Legacy> v;
607 for (unsigned i = 0; i < n; i++) {
608 v.push_back(Legacy());
609 }
610 bufferlist bl(n * sizeof(uint8_t));
611 ::encode(v, bl);
612 bufferlist segmented;
613 auto p = bl.begin();
614
615 auto sum = std::accumulate(segments.begin(), segments.end(), 0u);
224ce89b 616 assert(sum != 0u);
31f18b77
FG
617 for (auto i : segments) {
618 buffer::ptr seg;
619 p.copy_deep(bl.length() * i / sum, seg);
620 segmented.push_back(seg);
621 }
622 p.copy_all(segmented);
623 return segmented;
624}
625
626TEST(denc, no_copy_if_segmented_and_lengthy)
627{
628 static_assert(_denc::has_legacy_denc<Legacy>::value,
629 "Legacy do have legacy denc");
630 {
631 // use denc() which shallow_copy() if the buffer is small
632 constexpr unsigned N_COPIES = 42;
633 const vector<unsigned> segs{50, 50}; // half-half
634 bufferlist segmented = Legacy::encode_n(N_COPIES, segs);
635 ASSERT_GT(segmented.get_num_buffers(), 1u);
636 ASSERT_LT(segmented.length(), CEPH_PAGE_SIZE);
637 auto p = segmented.begin();
638 vector<Legacy> v;
639 // denc() is shared by encode() and decode(), so reset() right before
640 // decode()
641 Legacy::reset();
642 ::decode(v, p);
643 ASSERT_EQ(N_COPIES, v.size());
644 ASSERT_EQ(N_COPIES, Legacy::n_denc);
645 ASSERT_EQ(0u, Legacy::n_decode);
646 }
647 {
648 // use denc() which shallow_copy() if the buffer is not segmented and large
649 const unsigned N_COPIES = CEPH_PAGE_SIZE * 2;
650 const vector<unsigned> segs{100};
651 bufferlist segmented = Legacy::encode_n(N_COPIES, segs);
652 ASSERT_EQ(segmented.get_num_buffers(), 1u);
653 ASSERT_GT(segmented.length(), CEPH_PAGE_SIZE);
654 auto p = segmented.begin();
655 vector<Legacy> v;
656 Legacy::reset();
657 ::decode(v, p);
658 ASSERT_EQ(N_COPIES, v.size());
659 ASSERT_EQ(N_COPIES, Legacy::n_denc);
660 ASSERT_EQ(0u, Legacy::n_decode);
661 }
662 {
663 // use denc() which shallow_copy() if the buffer is segmented and large,
664 // but the total size of the chunks to be decoded is smallish.
665 bufferlist large_bl = Legacy::encode_n(CEPH_PAGE_SIZE * 2, {50, 50});
666 bufferlist small_bl = Legacy::encode_n(100, {50, 50});
667 bufferlist segmented;
668 segmented.append(large_bl);
669 segmented.append(small_bl);
670 ASSERT_GT(segmented.get_num_buffers(), 1u);
671 ASSERT_GT(segmented.length(), CEPH_PAGE_SIZE);
672 auto p = segmented.begin();
673 p.advance(large_bl.length());
674 ASSERT_LT(segmented.length() - p.get_off(), CEPH_PAGE_SIZE);
675 vector<Legacy> v;
676 Legacy::reset();
677 ::decode(v, p);
678 ASSERT_EQ(Legacy::n_denc, 100u);
679 ASSERT_EQ(0u, Legacy::n_decode);
680 }
681 {
682 // use decode() which avoids deep copy if the buffer is segmented and large
683 bufferlist small_bl = Legacy::encode_n(100, {50, 50});
684 bufferlist large_bl = Legacy::encode_n(CEPH_PAGE_SIZE * 2, {50, 50});
685 bufferlist segmented;
686 segmented.append(small_bl);
687 segmented.append(large_bl);
688 ASSERT_GT(segmented.get_num_buffers(), 1u);
689 ASSERT_GT(segmented.length(), CEPH_PAGE_SIZE);
690 auto p = segmented.begin();
691 p.advance(small_bl.length());
692 ASSERT_GT(segmented.length() - p.get_off(), CEPH_PAGE_SIZE);
693 vector<Legacy> v;
694 Legacy::reset();
695 ::decode(v, p);
696 ASSERT_EQ(0u, Legacy::n_denc);
697 ASSERT_EQ(CEPH_PAGE_SIZE * 2, Legacy::n_decode);
698 }
699}