]>
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; | |
32 | ::encode(v, bl); | |
33 | bufferlist::iterator p = bl.begin(); | |
34 | T out; | |
35 | ::decode(out, p); | |
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; | |
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 | ||
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 | } | |
126 | void decode(buffer::ptr::iterator &p) { | |
127 | p.advance(1); | |
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 | } | |
142 | void decode(buffer::ptr::iterator &p) { | |
143 | p.advance(1); | |
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; | |
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 | ||
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 { | |
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 | }; | |
200 | WRITE_CLASS_ENCODER(legacy_t) | |
201 | ||
202 | template<template<class> class C> | |
203 | void 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 | ||
234 | template<typename T> | |
235 | using default_vector = std::vector<T>; | |
236 | ||
237 | TEST(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 | ||
268 | template<typename T> | |
269 | using default_list = std::list<T>; | |
270 | ||
271 | TEST(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 | ||
291 | template<template<class> class C> | |
292 | void 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 | ||
318 | template<typename T> | |
319 | using default_set = std::set<T>; | |
320 | ||
321 | TEST(denc, set) | |
322 | { | |
323 | test_setlike<default_set>("std::set"); | |
324 | } | |
325 | ||
326 | template<typename T> | |
327 | using default_flat_set= boost::container::flat_set<T>; | |
328 | ||
329 | TEST(denc, flat_set) | |
330 | { | |
331 | test_setlike<default_flat_set>("std::set"); | |
332 | } | |
333 | ||
334 | struct 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 | }; | |
349 | WRITE_CLASS_DENC_BOUNDED(foo_t) | |
350 | ||
351 | struct 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 | }; | |
366 | WRITE_CLASS_DENC_BOUNDED(foo2_t) | |
367 | ||
368 | ||
369 | struct 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 | }; | |
382 | WRITE_CLASS_DENC_FEATURED_BOUNDED(bar_t) | |
383 | ||
384 | TEST(denc, foo) | |
385 | { | |
386 | foo_t a; | |
387 | test_denc(a); | |
388 | bufferlist bl; | |
389 | ::encode(a, bl); | |
390 | bl.hexdump(cout); | |
391 | } | |
392 | ||
393 | TEST(denc, bar) | |
394 | { | |
395 | bar_t a; | |
396 | test_denc_featured(a); | |
397 | } | |
398 | ||
399 | ||
400 | ||
401 | TEST(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 | ||
415 | template<template<class, class> class C> | |
416 | void 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 | ||
442 | template<typename U, typename V> | |
443 | using default_map = std::map<U, V>; | |
444 | ||
445 | TEST(denc, map) | |
446 | { | |
447 | test_common_maplike<default_map>("std::map"); | |
448 | } | |
449 | ||
450 | template<typename U, typename V> | |
451 | using default_flat_map = boost::container::flat_map<U, V>; | |
452 | ||
453 | TEST(denc, flat_map) | |
454 | { | |
455 | test_common_maplike<default_flat_map>("boost::container::flat_map"); | |
456 | } | |
457 | ||
458 | TEST(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 | ||
509 | TEST(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 | ||
524 | TEST(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 | ||
545 | TEST(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. | |
584 | struct 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 | }; | |
601 | WRITE_CLASS_DENC(Legacy) | |
602 | unsigned Legacy::n_denc = 0; | |
603 | unsigned Legacy::n_decode = 0; | |
604 | ||
605 | bufferlist 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); | |
616 | for (auto i : segments) { | |
617 | buffer::ptr seg; | |
618 | p.copy_deep(bl.length() * i / sum, seg); | |
619 | segmented.push_back(seg); | |
620 | } | |
621 | p.copy_all(segmented); | |
622 | return segmented; | |
623 | } | |
624 | ||
625 | TEST(denc, no_copy_if_segmented_and_lengthy) | |
626 | { | |
627 | static_assert(_denc::has_legacy_denc<Legacy>::value, | |
628 | "Legacy do have legacy denc"); | |
629 | { | |
630 | // use denc() which shallow_copy() if the buffer is small | |
631 | constexpr unsigned N_COPIES = 42; | |
632 | const vector<unsigned> segs{50, 50}; // half-half | |
633 | bufferlist segmented = Legacy::encode_n(N_COPIES, segs); | |
634 | ASSERT_GT(segmented.get_num_buffers(), 1u); | |
635 | ASSERT_LT(segmented.length(), CEPH_PAGE_SIZE); | |
636 | auto p = segmented.begin(); | |
637 | vector<Legacy> v; | |
638 | // denc() is shared by encode() and decode(), so reset() right before | |
639 | // decode() | |
640 | Legacy::reset(); | |
641 | ::decode(v, p); | |
642 | ASSERT_EQ(N_COPIES, v.size()); | |
643 | ASSERT_EQ(N_COPIES, Legacy::n_denc); | |
644 | ASSERT_EQ(0u, Legacy::n_decode); | |
645 | } | |
646 | { | |
647 | // use denc() which shallow_copy() if the buffer is not segmented and large | |
648 | const unsigned N_COPIES = CEPH_PAGE_SIZE * 2; | |
649 | const vector<unsigned> segs{100}; | |
650 | bufferlist segmented = Legacy::encode_n(N_COPIES, segs); | |
651 | ASSERT_EQ(segmented.get_num_buffers(), 1u); | |
652 | ASSERT_GT(segmented.length(), CEPH_PAGE_SIZE); | |
653 | auto p = segmented.begin(); | |
654 | vector<Legacy> v; | |
655 | Legacy::reset(); | |
656 | ::decode(v, p); | |
657 | ASSERT_EQ(N_COPIES, v.size()); | |
658 | ASSERT_EQ(N_COPIES, Legacy::n_denc); | |
659 | ASSERT_EQ(0u, Legacy::n_decode); | |
660 | } | |
661 | { | |
662 | // use denc() which shallow_copy() if the buffer is segmented and large, | |
663 | // but the total size of the chunks to be decoded is smallish. | |
664 | bufferlist large_bl = Legacy::encode_n(CEPH_PAGE_SIZE * 2, {50, 50}); | |
665 | bufferlist small_bl = Legacy::encode_n(100, {50, 50}); | |
666 | bufferlist segmented; | |
667 | segmented.append(large_bl); | |
668 | segmented.append(small_bl); | |
669 | ASSERT_GT(segmented.get_num_buffers(), 1u); | |
670 | ASSERT_GT(segmented.length(), CEPH_PAGE_SIZE); | |
671 | auto p = segmented.begin(); | |
672 | p.advance(large_bl.length()); | |
673 | ASSERT_LT(segmented.length() - p.get_off(), CEPH_PAGE_SIZE); | |
674 | vector<Legacy> v; | |
675 | Legacy::reset(); | |
676 | ::decode(v, p); | |
677 | ASSERT_EQ(Legacy::n_denc, 100u); | |
678 | ASSERT_EQ(0u, Legacy::n_decode); | |
679 | } | |
680 | { | |
681 | // use decode() which avoids deep copy if the buffer is segmented and large | |
682 | bufferlist small_bl = Legacy::encode_n(100, {50, 50}); | |
683 | bufferlist large_bl = Legacy::encode_n(CEPH_PAGE_SIZE * 2, {50, 50}); | |
684 | bufferlist segmented; | |
685 | segmented.append(small_bl); | |
686 | segmented.append(large_bl); | |
687 | ASSERT_GT(segmented.get_num_buffers(), 1u); | |
688 | ASSERT_GT(segmented.length(), CEPH_PAGE_SIZE); | |
689 | auto p = segmented.begin(); | |
690 | p.advance(small_bl.length()); | |
691 | ASSERT_GT(segmented.length() - p.get_off(), CEPH_PAGE_SIZE); | |
692 | vector<Legacy> v; | |
693 | Legacy::reset(); | |
694 | ::decode(v, p); | |
695 | ASSERT_EQ(0u, Legacy::n_denc); | |
696 | ASSERT_EQ(CEPH_PAGE_SIZE * 2, Legacy::n_decode); | |
697 | } | |
698 | } |