1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
4 * Ceph distributed storage system
6 * Copyright (C) 2016 Red Hat
8 * Author: Sage Weil <sage@redhat.com>
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.
20 #include "global/global_init.h"
21 #include "common/ceph_argparse.h"
22 #include "global/global_context.h"
23 #include "gtest/gtest.h"
25 #include "include/denc.h"
30 void test_encode_decode(T v) {
33 bufferlist::iterator p = bl.begin();
49 auto a = bl.get_contiguous_appender(s);
52 ASSERT_LE(bl.length(), s);
57 auto bpi = bl.front().begin();
60 ASSERT_EQ(bpi.get_pos(), bl.c_str() + bl.length());
63 test_encode_decode(v);
67 void test_encode_decode_featured(T v) {
70 bufferlist::iterator p = bl.begin();
77 void test_denc_featured(T v) {
86 auto a = bl.get_contiguous_appender(s);
89 ASSERT_LE(bl.length(), s);
94 auto bpi = bl.front().begin();
97 ASSERT_EQ(bpi.get_pos(), bl.c_str() + bl.length());
100 test_encode_decode_featured(v);
104 // hooks to count bound calls
107 int num_bound_encode = 0;
111 num_bound_encode = 0;
117 struct 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
122 void encode(buffer::list::contiguous_appender& p) const {
126 void decode(buffer::ptr::iterator &p) {
131 WRITE_CLASS_DENC(denc_counter_t)
133 struct 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
138 void encode(buffer::list::contiguous_appender& p) const {
142 void decode(buffer::ptr::iterator &p) {
147 WRITE_CLASS_DENC_BOUNDED(denc_counter_bounded_t)
149 TEST(denc, denc_counter)
151 denc_counter_t single, single2;
154 ::encode(single, bl);
155 ::decode(single2, bl);
157 ASSERT_EQ(counts.num_bound_encode, 1);
158 ASSERT_EQ(counts.num_encode, 1);
159 ASSERT_EQ(counts.num_decode, 1);
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);
177 string a, b("hi"), c("multi\nline\n");
185 void encode(bufferlist& bl) const {
188 void decode(bufferlist::iterator& p) {
192 legacy_t(int32_t i) : a(i) {}
193 friend bool operator<(const legacy_t& l, const legacy_t& r) {
196 friend bool operator==(const legacy_t& l, const legacy_t& r) {
200 WRITE_CLASS_ENCODER(legacy_t)
202 template<template<class> class C>
203 void test_common_veclist(const char* c) {
205 cout << c << "<std::string>" << std::endl;
214 cout << c << "<int32_t>" << std::endl;
222 cout << c << "<legacy_t>" << std::endl;
224 s.push_back(legacy_t(1));
225 s.push_back(legacy_t(2));
226 test_encode_decode(s);
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.)
235 using default_vector = std::vector<T>;
239 test_common_veclist<default_vector>("std::vector");
242 vector<denc_counter_t> v, v2;
249 ASSERT_EQ(counts.num_bound_encode, 100);
250 ASSERT_EQ(counts.num_encode, 100);
251 ASSERT_EQ(counts.num_decode, 100);
255 vector<denc_counter_bounded_t> v, v2;
262 ASSERT_EQ(counts.num_bound_encode, 1);
263 ASSERT_EQ(counts.num_encode, 100);
264 ASSERT_EQ(counts.num_decode, 100);
269 using default_list = std::list<T>;
273 test_common_veclist<default_list>("std::list");
276 list<denc_counter_bounded_t> l, l2;
277 for (unsigned i=0; i<100; ++i) {
278 l.emplace_back(denc_counter_bounded_t());
285 ASSERT_EQ(counts.num_bound_encode, 1);
286 ASSERT_EQ(counts.num_encode, 100);
287 ASSERT_EQ(counts.num_decode, 100);
291 template<template<class> class C>
292 void test_setlike(const char* c) {
294 cout << c << "<std::string>" << std::endl;
302 cout << c << "<int32_t>" << std::endl;
310 cout << c << "<legacy_t>" << std::endl;
312 s.insert(legacy_t(1));
313 s.insert(legacy_t(2));
314 test_encode_decode(s);
319 using default_set = std::set<T>;
323 test_setlike<default_set>("std::set");
327 using default_flat_set= boost::container::flat_set<T>;
331 test_setlike<default_flat_set>("std::set");
345 friend bool operator==(const foo_t& l, const foo_t& r) {
346 return l.a == r.a && l.b == r.b;
349 WRITE_CLASS_DENC_BOUNDED(foo_t)
362 friend bool operator==(const foo2_t& l, const foo2_t& r) {
363 return l.c == r.c && l.d == r.d;
366 WRITE_CLASS_DENC_BOUNDED(foo2_t)
373 DENC_FEATURED(bar_t, v, p, f) {
378 friend bool operator==(const bar_t& l, const bar_t& r) {
379 return l.a == r.a && l.b == r.b;
382 WRITE_CLASS_DENC_FEATURED_BOUNDED(bar_t)
396 test_denc_featured(a);
403 pair<int32_t,std::string> p;
406 auto a = bl.get_contiguous_appender(1000);
411 pair<int32_t,legacy_t> lp;
415 template<template<class, class> class C>
416 void test_common_maplike(const char* c) {
418 cout << c << "<std::string, foo_t>" << std::endl;
426 cout << c << "<std::string, bar_t>" << std::endl;
431 test_denc_featured(s);
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);
442 template<typename U, typename V>
443 using default_map = std::map<U, V>;
447 test_common_maplike<default_map>("std::map");
450 template<typename U, typename V>
451 using default_flat_map = boost::container::flat_map<U, V>;
455 test_common_maplike<default_flat_map>("boost::container::flat_map");
458 TEST(denc, bufferptr_shallow_and_deep) {
461 bufferptr p1("foo", 3);
464 auto a = bl.get_contiguous_appender(100);
469 cout << "bl is " << bl << std::endl;
471 ASSERT_EQ(3u, bl.get_num_buffers());
479 cout << "bl is " << bl << std::endl;
481 auto p = bl.front().begin();
487 ASSERT_EQ(3u, op.length());
488 ASSERT_EQ('f', op[0]);
489 memset(bl.c_str(), 0, bl.length());
495 cout << "bl is " << bl2 << std::endl;
497 auto p = bl2.front().begin_deep();
503 ASSERT_EQ('f', op[0]);
504 memset(bl2.c_str(), 1, bl2.length());
505 ASSERT_EQ('f', op[0]);
512 cout << "std::array<std::string, 3>" << std::endl;
513 std::array<std::string, 3> s = { "foo", "bar", "baz" };
518 cout << "std::array<uint32_t, 3>" << std::endl;
519 std::array<uint32_t, 3> s = { 1UL, 2UL, 3UL };
527 cout << "std::tuple<uint64_t, uint32_t>" << std::endl;
528 std::tuple<uint64_t, uint32_t> s(100ULL, 97UL);
533 cout << "std::tuple<std::string, uint3_t>" << std::endl;
534 std::tuple<std::string, uint32_t> s("foo", 97);
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)});
548 cout << "boost::optional<uint64_t>" << std::endl;
549 boost::optional<uint64_t> s = 97, t = boost::none;
555 cout << "boost::optional<std::string>" << std::endl;
556 boost::optional<std::string> s = std::string("Meow"), t = boost::none;
563 denc(boost::none, s);
569 auto a = bl.get_contiguous_appender(s);
570 denc(boost::none, a);
572 ASSERT_LE(bl.length(), s);
575 boost::optional<uint32_t> out = 5;
576 auto bpi = bl.front().begin();
579 ASSERT_EQ(bpi.get_pos(), bl.c_str() + bl.length());
583 // unlike legacy_t, Legacy supports denc() also.
585 static unsigned n_denc;
586 static unsigned n_decode;
592 void decode(buffer::list::iterator& p) {
596 static void reset() {
597 n_denc = n_decode = 0;
599 static bufferlist encode_n(unsigned n, const vector<unsigned>& segments);
601 WRITE_CLASS_DENC(Legacy)
602 unsigned Legacy::n_denc = 0;
603 unsigned Legacy::n_decode = 0;
605 bufferlist Legacy::encode_n(unsigned n, const vector<unsigned>& segments) {
607 for (unsigned i = 0; i < n; i++) {
608 v.push_back(Legacy());
610 bufferlist bl(n * sizeof(uint8_t));
612 bufferlist segmented;
615 auto sum = std::accumulate(segments.begin(), segments.end(), 0u);
617 for (auto i : segments) {
619 p.copy_deep(bl.length() * i / sum, seg);
620 segmented.push_back(seg);
622 p.copy_all(segmented);
626 TEST(denc, no_copy_if_segmented_and_lengthy)
628 static_assert(_denc::has_legacy_denc<Legacy>::value,
629 "Legacy do have legacy denc");
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();
639 // denc() is shared by encode() and decode(), so reset() right before
643 ASSERT_EQ(N_COPIES, v.size());
644 ASSERT_EQ(N_COPIES, Legacy::n_denc);
645 ASSERT_EQ(0u, Legacy::n_decode);
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();
658 ASSERT_EQ(N_COPIES, v.size());
659 ASSERT_EQ(N_COPIES, Legacy::n_denc);
660 ASSERT_EQ(0u, Legacy::n_decode);
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);
678 ASSERT_EQ(Legacy::n_denc, 100u);
679 ASSERT_EQ(0u, Legacy::n_decode);
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);
696 ASSERT_EQ(0u, Legacy::n_denc);
697 ASSERT_EQ(CEPH_PAGE_SIZE * 2, Legacy::n_decode);