]> git.proxmox.com Git - ceph.git/blame - ceph/src/test/test_denc.cc
buildsys: switch source download to quincy
[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;
11fdf7f2
TL
32 encode(v, bl);
33 auto p = bl.cbegin();
7c673cae 34 T out;
11fdf7f2 35 decode(out, p);
7c673cae
FG
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;
11fdf7f2
TL
69 encode(v, bl, 123);
70 auto p = bl.cbegin();
7c673cae 71 T out;
11fdf7f2 72 decode(out, p);
7c673cae
FG
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 }
11fdf7f2 126 void decode(buffer::ptr::const_iterator &p) {
9f95a23c 127 p += 1;
7c673cae
FG
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 }
11fdf7f2 142 void decode(buffer::ptr::const_iterator &p) {
9f95a23c 143 p += 1;
7c673cae
FG
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;
11fdf7f2
TL
154 encode(single, bl);
155 decode(single2, bl);
7c673cae
FG
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 {
11fdf7f2
TL
186 using ceph::encode;
187 encode(a, bl);
7c673cae 188 }
11fdf7f2
TL
189 void decode(bufferlist::const_iterator& p) {
190 using ceph::decode;
191 decode(a, p);
7c673cae
FG
192 }
193 legacy_t() {}
11fdf7f2 194 explicit legacy_t(int32_t i) : a(i) {}
7c673cae
FG
195 friend bool operator<(const legacy_t& l, const legacy_t& r) {
196 return l.a < r.a;
197 }
198 friend bool operator==(const legacy_t& l, const legacy_t& r) {
199 return l.a == r.a;
200 }
201};
202WRITE_CLASS_ENCODER(legacy_t)
203
204template<template<class> class C>
205void test_common_veclist(const char* c) {
206 {
207 cout << c << "<std::string>" << std::endl;
208 C<std::string> s;
209 s.push_back("foo");
210 s.push_back("bar");
211 s.push_back("baz");
212 counts.reset();
213 test_denc(s);
214 }
215 {
216 cout << c << "<int32_t>" << std::endl;
217 C<int32_t> s;
218 s.push_back(1);
219 s.push_back(2);
220 s.push_back(3);
221 test_denc(s);
222 }
223 {
224 cout << c << "<legacy_t>" << std::endl;
225 C<legacy_t> s;
226 s.push_back(legacy_t(1));
227 s.push_back(legacy_t(2));
228 test_encode_decode(s);
229 }
230}
231
232// We only care about specializing the type, all other template
233// parameters should have the default values. (Like first-class
234// functions, first-class templates do not bring their defaults.)
235
236template<typename T>
237using default_vector = std::vector<T>;
238
239TEST(denc, vector)
240{
241 test_common_veclist<default_vector>("std::vector");
242 {
243 counts.reset();
244 vector<denc_counter_t> v, v2;
245 v.resize(100);
246 {
247 bufferlist bl;
11fdf7f2
TL
248 encode(v, bl);
249 decode(v2, bl);
7c673cae
FG
250 }
251 ASSERT_EQ(counts.num_bound_encode, 100);
252 ASSERT_EQ(counts.num_encode, 100);
253 ASSERT_EQ(counts.num_decode, 100);
254 }
255 {
256 counts.reset();
257 vector<denc_counter_bounded_t> v, v2;
258 v.resize(100);
259 {
260 bufferlist bl;
11fdf7f2
TL
261 encode(v, bl);
262 decode(v2, bl);
7c673cae
FG
263 }
264 ASSERT_EQ(counts.num_bound_encode, 1);
265 ASSERT_EQ(counts.num_encode, 100);
266 ASSERT_EQ(counts.num_decode, 100);
267 }
268}
269
270template<typename T>
271using default_list = std::list<T>;
272
273TEST(denc, list)
274{
275 test_common_veclist<default_list>("std::list");
276 {
277 counts.reset();
278 list<denc_counter_bounded_t> l, l2;
279 for (unsigned i=0; i<100; ++i) {
280 l.emplace_back(denc_counter_bounded_t());
281 }
282 {
283 bufferlist bl;
11fdf7f2
TL
284 encode(l, bl);
285 decode(l2, bl);
7c673cae
FG
286 }
287 ASSERT_EQ(counts.num_bound_encode, 1);
288 ASSERT_EQ(counts.num_encode, 100);
289 ASSERT_EQ(counts.num_decode, 100);
290 }
291}
292
293template<template<class> class C>
294void test_setlike(const char* c) {
295 {
296 cout << c << "<std::string>" << std::endl;
297 C<std::string> s;
298 s.insert("foo");
299 s.insert("bar");
300 s.insert("baz");
301 test_denc(s);
302 }
303 {
304 cout << c << "<int32_t>" << std::endl;
305 C<int32_t> s;
306 s.insert(1);
307 s.insert(2);
308 s.insert(3);
309 test_denc(s);
310 }
311 {
312 cout << c << "<legacy_t>" << std::endl;
313 C<legacy_t> s;
314 s.insert(legacy_t(1));
315 s.insert(legacy_t(2));
316 test_encode_decode(s);
317 }
318}
319
320template<typename T>
321using default_set = std::set<T>;
322
323TEST(denc, set)
324{
325 test_setlike<default_set>("std::set");
326}
327
328template<typename T>
329using default_flat_set= boost::container::flat_set<T>;
330
331TEST(denc, flat_set)
332{
333 test_setlike<default_flat_set>("std::set");
334}
335
336struct foo_t {
337 int32_t a = 0;
338 uint64_t b = 123;
339
340 DENC(foo_t, v, p) {
341 DENC_START(1, 1, p);
342 ::denc(v.a, p);
343 ::denc(v.b, p);
344 DENC_FINISH(p);
345 }
346
347 friend bool operator==(const foo_t& l, const foo_t& r) {
348 return l.a == r.a && l.b == r.b;
349 }
350};
351WRITE_CLASS_DENC_BOUNDED(foo_t)
352
353struct foo2_t {
354 int32_t c = 0;
355 uint64_t d = 123;
356
357 DENC(foo2_t, v, p) {
358 DENC_START(1, 1, p);
359 ::denc(v.c, p);
360 ::denc(v.d, p);
361 DENC_FINISH(p);
362 }
363
364 friend bool operator==(const foo2_t& l, const foo2_t& r) {
365 return l.c == r.c && l.d == r.d;
366 }
367};
368WRITE_CLASS_DENC_BOUNDED(foo2_t)
369
370
371struct bar_t {
372 int32_t a = 0;
373 uint64_t b = 123;
374
375 DENC_FEATURED(bar_t, v, p, f) {
376 ::denc(v.a, p, f);
377 ::denc(v.b, p, f);
378 }
379
380 friend bool operator==(const bar_t& l, const bar_t& r) {
381 return l.a == r.a && l.b == r.b;
382 }
383};
384WRITE_CLASS_DENC_FEATURED_BOUNDED(bar_t)
385
386TEST(denc, foo)
387{
388 foo_t a;
389 test_denc(a);
390 bufferlist bl;
11fdf7f2 391 encode(a, bl);
7c673cae
FG
392 bl.hexdump(cout);
393}
394
395TEST(denc, bar)
396{
397 bar_t a;
398 test_denc_featured(a);
399}
400
401
402
403TEST(denc, pair)
404{
405 pair<int32_t,std::string> p;
406 bufferlist bl;
407 {
408 auto a = bl.get_contiguous_appender(1000);
409 denc(p, a);
11fdf7f2 410 encode(p, bl);
7c673cae
FG
411 }
412
413 pair<int32_t,legacy_t> lp;
11fdf7f2 414 encode(lp, bl);
7c673cae
FG
415}
416
417template<template<class, class> class C>
418void test_common_maplike(const char* c) {
419 {
420 cout << c << "<std::string, foo_t>" << std::endl;
421 C<string, foo_t> s;
422 s["foo"] = foo_t();
423 s["bar"] = foo_t();
424 s["baz"] = foo_t();
425 test_denc(s);
426 }
427 {
428 cout << c << "<std::string, bar_t>" << std::endl;
429 C<string, bar_t> s;
430 s["foo"] = bar_t();
431 s["bar"] = bar_t();
432 s["baz"] = bar_t();
433 test_denc_featured(s);
434 }
435 {
436 cout << c << "<std::string, legacy_t>" << std::endl;
437 C<std::string, legacy_t> s;
438 s["foo"] = legacy_t(1);
439 s["bar"] = legacy_t(2);
440 test_encode_decode(s);
441 }
442}
443
444template<typename U, typename V>
445using default_map = std::map<U, V>;
446
447TEST(denc, map)
448{
449 test_common_maplike<default_map>("std::map");
450}
451
452template<typename U, typename V>
453using default_flat_map = boost::container::flat_map<U, V>;
454
455TEST(denc, flat_map)
456{
457 test_common_maplike<default_flat_map>("boost::container::flat_map");
458}
459
460TEST(denc, bufferptr_shallow_and_deep) {
461 // shallow encode
462 int32_t i = 1;
463 bufferptr p1("foo", 3);
464 bufferlist bl;
465 {
466 auto a = bl.get_contiguous_appender(100);
467 denc(i, a);
468 denc(p1, a);
469 denc(i, a);
470 }
471 cout << "bl is " << bl << std::endl;
472 bl.hexdump(cout);
473 ASSERT_EQ(3u, bl.get_num_buffers());
474
475 bufferlist bl2 = bl;
476 bl.rebuild();
477 bl2.rebuild();
478
479 // shallow decode
480 {
481 cout << "bl is " << bl << std::endl;
482 bl.hexdump(cout);
483 auto p = bl.front().begin();
484 bufferptr op;
485 int32_t i;
486 denc(i, p);
487 denc(op, p);
488 denc(i, p);
489 ASSERT_EQ(3u, op.length());
490 ASSERT_EQ('f', op[0]);
491 memset(bl.c_str(), 0, bl.length());
492 ASSERT_EQ(0, op[0]);
493 }
494
495 // deep decode
496 {
497 cout << "bl is " << bl2 << std::endl;
498 bl2.hexdump(cout);
499 auto p = bl2.front().begin_deep();
500 bufferptr op;
501 int32_t i;
502 denc(i, p);
503 denc(op, p);
504 denc(i, p);
505 ASSERT_EQ('f', op[0]);
506 memset(bl2.c_str(), 1, bl2.length());
507 ASSERT_EQ('f', op[0]);
508 }
509}
510
511TEST(denc, array)
512{
513 {
514 cout << "std::array<std::string, 3>" << std::endl;
515 std::array<std::string, 3> s = { "foo", "bar", "baz" };
516 counts.reset();
517 test_denc(s);
518 }
519 {
520 cout << "std::array<uint32_t, 3>" << std::endl;
521 std::array<uint32_t, 3> s = { 1UL, 2UL, 3UL };
522 test_denc(s);
523 }
524}
525
526TEST(denc, tuple)
527{
528 {
529 cout << "std::tuple<uint64_t, uint32_t>" << std::endl;
530 std::tuple<uint64_t, uint32_t> s(100ULL, 97UL);
531 counts.reset();
532 test_denc(s);
533 }
534 {
535 cout << "std::tuple<std::string, uint3_t>" << std::endl;
536 std::tuple<std::string, uint32_t> s("foo", 97);
537 test_denc(s);
538 }
539 {
540 cout << "std::tuple<std::string, std::set<uint32_t>>" << std::endl;
541 std::tuple<std::string, std::set<uint32_t>> s(
542 "bar", std::set<uint32_t>{uint32_t(1), uint32_t(2), uint32_t(3)});
543 test_denc(s);
544 }
545}
546
547TEST(denc, optional)
548{
549 {
550 cout << "boost::optional<uint64_t>" << std::endl;
551 boost::optional<uint64_t> s = 97, t = boost::none;
552 counts.reset();
553 test_denc(s);
554 test_denc(t);
555 }
556 {
557 cout << "boost::optional<std::string>" << std::endl;
558 boost::optional<std::string> s = std::string("Meow"), t = boost::none;
559 counts.reset();
560 test_denc(s);
561 test_denc(t);
562 }
563 {
564 size_t s = 0;
565 denc(boost::none, s);
566 ASSERT_NE(s, 0u);
567
568 // encode
569 bufferlist bl;
570 {
571 auto a = bl.get_contiguous_appender(s);
572 denc(boost::none, a);
573 }
574 ASSERT_LE(bl.length(), s);
575
576 bl.rebuild();
577 boost::optional<uint32_t> out = 5;
578 auto bpi = bl.front().begin();
579 denc(out, bpi);
580 ASSERT_FALSE(!!out);
581 ASSERT_EQ(bpi.get_pos(), bl.c_str() + bl.length());
582 }
583}
31f18b77 584
11fdf7f2
TL
585TEST(denc, stdoptional)
586{
587 {
588 cout << "std::optional<uint64_t>" << std::endl;
589 std::optional<uint64_t> s = 97, t = std::nullopt;
590 counts.reset();
591 test_denc(s);
592 test_denc(t);
593 }
594 {
595 cout << "std::optional<std::string>" << std::endl;
596 std::optional<std::string> s = std::string("Meow"), t = std::nullopt;
597 counts.reset();
598 test_denc(s);
599 test_denc(t);
600 }
601 {
602 size_t s = 0;
603 denc(std::nullopt, s);
604 ASSERT_NE(s, 0u);
605
606 // encode
607 bufferlist bl;
608 {
609 auto a = bl.get_contiguous_appender(s);
610 denc(std::nullopt, a);
611 }
612 ASSERT_LE(bl.length(), s);
613
614 bl.rebuild();
615 std::optional<uint32_t> out = 5;
616 auto bpi = bl.front().begin();
617 denc(out, bpi);
618 ASSERT_FALSE(!!out);
619 ASSERT_EQ(bpi.get_pos(), bl.c_str() + bl.length());
620 }
621}
622
31f18b77
FG
623// unlike legacy_t, Legacy supports denc() also.
624struct Legacy {
625 static unsigned n_denc;
626 static unsigned n_decode;
627 uint8_t value = 0;
628 DENC(Legacy, v, p) {
629 n_denc++;
630 denc(v.value, p);
631 }
11fdf7f2 632 void decode(buffer::list::const_iterator& p) {
31f18b77 633 n_decode++;
11fdf7f2
TL
634 using ceph::decode;
635 decode(value, p);
31f18b77
FG
636 }
637 static void reset() {
638 n_denc = n_decode = 0;
639 }
640 static bufferlist encode_n(unsigned n, const vector<unsigned>& segments);
641};
642WRITE_CLASS_DENC(Legacy)
643unsigned Legacy::n_denc = 0;
644unsigned Legacy::n_decode = 0;
645
646bufferlist Legacy::encode_n(unsigned n, const vector<unsigned>& segments) {
647 vector<Legacy> v;
648 for (unsigned i = 0; i < n; i++) {
649 v.push_back(Legacy());
650 }
651 bufferlist bl(n * sizeof(uint8_t));
11fdf7f2
TL
652 using ceph::encode;
653 encode(v, bl);
31f18b77
FG
654 bufferlist segmented;
655 auto p = bl.begin();
656
657 auto sum = std::accumulate(segments.begin(), segments.end(), 0u);
11fdf7f2 658 ceph_assert(sum != 0u);
31f18b77
FG
659 for (auto i : segments) {
660 buffer::ptr seg;
661 p.copy_deep(bl.length() * i / sum, seg);
662 segmented.push_back(seg);
663 }
664 p.copy_all(segmented);
665 return segmented;
666}
667
668TEST(denc, no_copy_if_segmented_and_lengthy)
669{
670 static_assert(_denc::has_legacy_denc<Legacy>::value,
671 "Legacy do have legacy denc");
672 {
673 // use denc() which shallow_copy() if the buffer is small
674 constexpr unsigned N_COPIES = 42;
675 const vector<unsigned> segs{50, 50}; // half-half
676 bufferlist segmented = Legacy::encode_n(N_COPIES, segs);
677 ASSERT_GT(segmented.get_num_buffers(), 1u);
678 ASSERT_LT(segmented.length(), CEPH_PAGE_SIZE);
11fdf7f2 679 auto p = segmented.cbegin();
31f18b77
FG
680 vector<Legacy> v;
681 // denc() is shared by encode() and decode(), so reset() right before
682 // decode()
683 Legacy::reset();
11fdf7f2 684 decode(v, p);
31f18b77
FG
685 ASSERT_EQ(N_COPIES, v.size());
686 ASSERT_EQ(N_COPIES, Legacy::n_denc);
687 ASSERT_EQ(0u, Legacy::n_decode);
688 }
689 {
690 // use denc() which shallow_copy() if the buffer is not segmented and large
691 const unsigned N_COPIES = CEPH_PAGE_SIZE * 2;
692 const vector<unsigned> segs{100};
693 bufferlist segmented = Legacy::encode_n(N_COPIES, segs);
694 ASSERT_EQ(segmented.get_num_buffers(), 1u);
695 ASSERT_GT(segmented.length(), CEPH_PAGE_SIZE);
11fdf7f2 696 auto p = segmented.cbegin();
31f18b77
FG
697 vector<Legacy> v;
698 Legacy::reset();
11fdf7f2 699 decode(v, p);
31f18b77
FG
700 ASSERT_EQ(N_COPIES, v.size());
701 ASSERT_EQ(N_COPIES, Legacy::n_denc);
702 ASSERT_EQ(0u, Legacy::n_decode);
703 }
704 {
705 // use denc() which shallow_copy() if the buffer is segmented and large,
706 // but the total size of the chunks to be decoded is smallish.
707 bufferlist large_bl = Legacy::encode_n(CEPH_PAGE_SIZE * 2, {50, 50});
708 bufferlist small_bl = Legacy::encode_n(100, {50, 50});
709 bufferlist segmented;
710 segmented.append(large_bl);
711 segmented.append(small_bl);
712 ASSERT_GT(segmented.get_num_buffers(), 1u);
713 ASSERT_GT(segmented.length(), CEPH_PAGE_SIZE);
11fdf7f2 714 auto p = segmented.cbegin();
9f95a23c 715 p += large_bl.length();
31f18b77
FG
716 ASSERT_LT(segmented.length() - p.get_off(), CEPH_PAGE_SIZE);
717 vector<Legacy> v;
718 Legacy::reset();
11fdf7f2 719 decode(v, p);
31f18b77
FG
720 ASSERT_EQ(Legacy::n_denc, 100u);
721 ASSERT_EQ(0u, Legacy::n_decode);
722 }
723 {
724 // use decode() which avoids deep copy if the buffer is segmented and large
725 bufferlist small_bl = Legacy::encode_n(100, {50, 50});
726 bufferlist large_bl = Legacy::encode_n(CEPH_PAGE_SIZE * 2, {50, 50});
727 bufferlist segmented;
728 segmented.append(small_bl);
729 segmented.append(large_bl);
730 ASSERT_GT(segmented.get_num_buffers(), 1u);
731 ASSERT_GT(segmented.length(), CEPH_PAGE_SIZE);
11fdf7f2 732 auto p = segmented.cbegin();
9f95a23c 733 p += small_bl.length();
31f18b77
FG
734 ASSERT_GT(segmented.length() - p.get_off(), CEPH_PAGE_SIZE);
735 vector<Legacy> v;
736 Legacy::reset();
11fdf7f2 737 decode(v, p);
31f18b77
FG
738 ASSERT_EQ(0u, Legacy::n_denc);
739 ASSERT_EQ(CEPH_PAGE_SIZE * 2, Legacy::n_decode);
740 }
741}