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