]>
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 | ||
27 | // test helpers | |
28 | ||
29 | template<typename T> | |
30 | void 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 | ||
39 | template<typename T> | |
40 | void 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 | ||
66 | template<typename T> | |
67 | void 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 | ||
76 | template<typename T> | |
77 | void 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 | ||
106 | struct 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 | ||
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 | |
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 | }; | |
131 | WRITE_CLASS_DENC(denc_counter_t) | |
132 | ||
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 | |
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 | }; | |
147 | WRITE_CLASS_DENC_BOUNDED(denc_counter_bounded_t) | |
148 | ||
149 | TEST(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 | ||
163 | TEST(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 | ||
175 | TEST(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 | ||
183 | struct 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 | }; | |
202 | WRITE_CLASS_ENCODER(legacy_t) | |
203 | ||
204 | template<template<class> class C> | |
205 | void 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 | ||
236 | template<typename T> | |
237 | using default_vector = std::vector<T>; | |
238 | ||
239 | TEST(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 | ||
270 | template<typename T> | |
271 | using default_list = std::list<T>; | |
272 | ||
273 | TEST(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 | ||
293 | template<template<class> class C> | |
294 | void 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 | ||
320 | template<typename T> | |
321 | using default_set = std::set<T>; | |
322 | ||
323 | TEST(denc, set) | |
324 | { | |
325 | test_setlike<default_set>("std::set"); | |
326 | } | |
327 | ||
328 | template<typename T> | |
329 | using default_flat_set= boost::container::flat_set<T>; | |
330 | ||
331 | TEST(denc, flat_set) | |
332 | { | |
333 | test_setlike<default_flat_set>("std::set"); | |
334 | } | |
335 | ||
336 | struct 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 | }; | |
351 | WRITE_CLASS_DENC_BOUNDED(foo_t) | |
352 | ||
353 | struct 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 | }; | |
368 | WRITE_CLASS_DENC_BOUNDED(foo2_t) | |
369 | ||
370 | ||
371 | struct 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 | }; | |
384 | WRITE_CLASS_DENC_FEATURED_BOUNDED(bar_t) | |
385 | ||
386 | TEST(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 | ||
395 | TEST(denc, bar) | |
396 | { | |
397 | bar_t a; | |
398 | test_denc_featured(a); | |
399 | } | |
400 | ||
401 | ||
402 | ||
403 | TEST(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 | ||
417 | template<template<class, class> class C> | |
418 | void 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 | ||
444 | template<typename U, typename V> | |
445 | using default_map = std::map<U, V>; | |
446 | ||
447 | TEST(denc, map) | |
448 | { | |
449 | test_common_maplike<default_map>("std::map"); | |
450 | } | |
451 | ||
452 | template<typename U, typename V> | |
453 | using default_flat_map = boost::container::flat_map<U, V>; | |
454 | ||
455 | TEST(denc, flat_map) | |
456 | { | |
457 | test_common_maplike<default_flat_map>("boost::container::flat_map"); | |
458 | } | |
459 | ||
460 | TEST(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 | ||
511 | TEST(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 | ||
526 | TEST(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 | ||
547 | TEST(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 |
585 | TEST(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. |
624 | struct 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 | }; | |
642 | WRITE_CLASS_DENC(Legacy) | |
643 | unsigned Legacy::n_denc = 0; | |
644 | unsigned Legacy::n_decode = 0; | |
645 | ||
646 | bufferlist 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 | ||
668 | TEST(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 | } |