1 | /* |
2 | * Copyright 2013-present Facebook, Inc. |
3 | * |
4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
5 | * you may not use this file except in compliance with the License. |
6 | * You may obtain a copy of the License at |
7 | * |
8 | * http://www.apache.org/licenses/LICENSE-2.0 |
9 | * |
10 | * Unless required by applicable law or agreed to in writing, software |
11 | * distributed under the License is distributed on an "AS IS" BASIS, |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. |
15 | */ |
16 | |
17 | #include <folly/io/IOBuf.h> |
18 | #include <folly/io/TypedIOBuf.h> |
19 | |
20 | #include <cstddef> |
21 | |
22 | #include <boost/random.hpp> |
23 | |
24 | #include <folly/Range.h> |
25 | #include <folly/memory/Malloc.h> |
26 | #include <folly/portability/GTest.h> |
27 | |
28 | using folly::ByteRange; |
29 | using folly::fbstring; |
30 | using folly::fbvector; |
31 | using folly::IOBuf; |
32 | using folly::ordering; |
33 | using folly::StringPiece; |
34 | using folly::TypedIOBuf; |
35 | using std::unique_ptr; |
36 | |
37 | void append(std::unique_ptr<IOBuf>& buf, StringPiece str) { |
38 | EXPECT_LE(str.size(), buf->tailroom()); |
39 | memcpy(buf->writableData(), str.data(), str.size()); |
40 | buf->append(str.size()); |
41 | } |
42 | |
43 | void prepend(std::unique_ptr<IOBuf>& buf, StringPiece str) { |
44 | EXPECT_LE(str.size(), buf->headroom()); |
45 | memcpy(buf->writableData() - str.size(), str.data(), str.size()); |
46 | buf->prepend(str.size()); |
47 | } |
48 | |
49 | TEST(IOBuf, Simple) { |
50 | unique_ptr<IOBuf> buf(IOBuf::create(100)); |
51 | uint32_t cap = buf->capacity(); |
52 | EXPECT_LE(100, cap); |
53 | EXPECT_EQ(0, buf->headroom()); |
54 | EXPECT_EQ(0, buf->length()); |
55 | EXPECT_EQ(cap, buf->tailroom()); |
56 | |
57 | append(buf, "world" ); |
58 | buf->advance(10); |
59 | EXPECT_EQ(10, buf->headroom()); |
60 | EXPECT_EQ(5, buf->length()); |
61 | EXPECT_EQ(cap - 15, buf->tailroom()); |
62 | |
63 | prepend(buf, "hello " ); |
64 | EXPECT_EQ(4, buf->headroom()); |
65 | EXPECT_EQ(11, buf->length()); |
66 | EXPECT_EQ(cap - 15, buf->tailroom()); |
67 | |
68 | const char* p = reinterpret_cast<const char*>(buf->data()); |
69 | EXPECT_EQ("hello world" , std::string(p, buf->length())); |
70 | |
71 | buf->clear(); |
72 | EXPECT_EQ(0, buf->headroom()); |
73 | EXPECT_EQ(0, buf->length()); |
74 | EXPECT_EQ(cap, buf->tailroom()); |
75 | } |
76 | |
77 | void testAllocSize(uint32_t requestedCapacity) { |
78 | unique_ptr<IOBuf> iobuf(IOBuf::create(requestedCapacity)); |
79 | EXPECT_GE(iobuf->capacity(), requestedCapacity); |
80 | } |
81 | |
82 | TEST(IOBuf, AllocSizes) { |
83 | // Try with a small allocation size that should fit in the internal buffer |
84 | testAllocSize(28); |
85 | |
86 | // Try with a large allocation size that will require an external buffer. |
87 | testAllocSize(9000); |
88 | |
89 | // 220 bytes is currently the cutoff |
90 | // (It would be nice to use the IOBuf::kMaxInternalDataSize constant, |
91 | // but it's private and it doesn't seem worth making it public just for this |
92 | // test code.) |
93 | testAllocSize(220); |
94 | testAllocSize(219); |
95 | testAllocSize(221); |
96 | } |
97 | |
98 | void deleteArrayBuffer(void* buf, void* arg) { |
99 | uint32_t* deleteCount = static_cast<uint32_t*>(arg); |
100 | ++(*deleteCount); |
101 | uint8_t* bufPtr = static_cast<uint8_t*>(buf); |
102 | delete[] bufPtr; |
103 | } |
104 | |
105 | TEST(IOBuf, TakeOwnership) { |
106 | uint32_t size1 = 99; |
107 | uint8_t* buf1 = static_cast<uint8_t*>(malloc(size1)); |
108 | unique_ptr<IOBuf> iobuf1(IOBuf::takeOwnership(buf1, size1)); |
109 | EXPECT_EQ(buf1, iobuf1->data()); |
110 | EXPECT_EQ(size1, iobuf1->length()); |
111 | EXPECT_EQ(buf1, iobuf1->buffer()); |
112 | EXPECT_EQ(size1, iobuf1->capacity()); |
113 | |
114 | uint32_t deleteCount = 0; |
115 | uint32_t size2 = 4321; |
116 | uint8_t* buf2 = new uint8_t[size2]; |
117 | unique_ptr<IOBuf> iobuf2( |
118 | IOBuf::takeOwnership(buf2, size2, deleteArrayBuffer, &deleteCount)); |
119 | EXPECT_EQ(buf2, iobuf2->data()); |
120 | EXPECT_EQ(size2, iobuf2->length()); |
121 | EXPECT_EQ(buf2, iobuf2->buffer()); |
122 | EXPECT_EQ(size2, iobuf2->capacity()); |
123 | EXPECT_EQ(0, deleteCount); |
124 | iobuf2.reset(); |
125 | EXPECT_EQ(1, deleteCount); |
126 | |
127 | deleteCount = 0; |
128 | uint32_t size3 = 3456; |
129 | uint8_t* buf3 = new uint8_t[size3]; |
130 | uint32_t length3 = 48; |
131 | unique_ptr<IOBuf> iobuf3(IOBuf::takeOwnership( |
132 | buf3, size3, length3, deleteArrayBuffer, &deleteCount)); |
133 | EXPECT_EQ(buf3, iobuf3->data()); |
134 | EXPECT_EQ(length3, iobuf3->length()); |
135 | EXPECT_EQ(buf3, iobuf3->buffer()); |
136 | EXPECT_EQ(size3, iobuf3->capacity()); |
137 | EXPECT_EQ(0, deleteCount); |
138 | iobuf3.reset(); |
139 | EXPECT_EQ(1, deleteCount); |
140 | |
141 | deleteCount = 0; |
142 | { |
143 | uint32_t size4 = 1234; |
144 | uint8_t* buf4 = new uint8_t[size4]; |
145 | uint32_t length4 = 48; |
146 | IOBuf iobuf4( |
147 | IOBuf::TAKE_OWNERSHIP, |
148 | buf4, |
149 | size4, |
150 | length4, |
151 | deleteArrayBuffer, |
152 | &deleteCount); |
153 | EXPECT_EQ(buf4, iobuf4.data()); |
154 | EXPECT_EQ(length4, iobuf4.length()); |
155 | EXPECT_EQ(buf4, iobuf4.buffer()); |
156 | EXPECT_EQ(size4, iobuf4.capacity()); |
157 | |
158 | IOBuf iobuf5 = std::move(iobuf4); |
159 | EXPECT_EQ(buf4, iobuf5.data()); |
160 | EXPECT_EQ(length4, iobuf5.length()); |
161 | EXPECT_EQ(buf4, iobuf5.buffer()); |
162 | EXPECT_EQ(size4, iobuf5.capacity()); |
163 | EXPECT_EQ(0, deleteCount); |
164 | } |
165 | EXPECT_EQ(1, deleteCount); |
166 | { |
167 | uint32_t size = 2; |
168 | uint8_t* buf = static_cast<uint8_t*>(malloc(size)); |
169 | buf[0] = 'A'; |
170 | unique_ptr<IOBuf> iobuf(IOBuf::takeOwnership(buf, size, 1)); |
171 | fbstring str = iobuf->moveToFbString(); |
172 | EXPECT_EQ(str, "A" ); |
173 | } |
174 | } |
175 | |
176 | TEST(IOBuf, GetUserData) { |
177 | { |
178 | const uint32_t size = 1234; |
179 | uint8_t data[size]; |
180 | unique_ptr<IOBuf> buf1(IOBuf::wrapBuffer(data, size)); |
181 | EXPECT_EQ(buf1->getUserData(), nullptr); |
182 | } |
183 | |
184 | { |
185 | size_t val = 0; |
186 | uint32_t size = 4321; |
187 | uint8_t* data = static_cast<uint8_t*>(malloc(size)); |
188 | unique_ptr<IOBuf> buf2(IOBuf::takeOwnership( |
189 | data, |
190 | size, |
191 | [](void* buf, void* userData) { |
192 | EXPECT_EQ(*static_cast<size_t*>(userData), 400); |
193 | free(buf); |
194 | }, |
195 | &val)); |
196 | EXPECT_EQ(buf2->getUserData(), &val); |
197 | val = 200; |
198 | EXPECT_EQ(*static_cast<size_t*>(buf2->getUserData()), 200); |
199 | val = 400; |
200 | } |
201 | } |
202 | |
203 | TEST(IOBuf, GetFreeFn) { |
204 | const uint32_t size = 4576; |
205 | uint8_t* data = static_cast<uint8_t*>(malloc(size)); |
206 | folly::IOBuf::FreeFunction someFreeFn = [](void* buf, void* userData) { |
207 | EXPECT_EQ(buf, userData); |
208 | free(userData); |
209 | }; |
210 | |
211 | unique_ptr<IOBuf> someBuf(IOBuf::wrapBuffer(data, size)); |
212 | unique_ptr<IOBuf> someOtherBuf( |
213 | IOBuf::takeOwnership(data, size, someFreeFn, data)); |
214 | |
215 | EXPECT_EQ(someBuf->getFreeFn(), nullptr); |
216 | EXPECT_EQ(someOtherBuf->getFreeFn(), someFreeFn); |
217 | } |
218 | |
219 | TEST(IOBuf, WrapBuffer) { |
220 | const uint32_t size1 = 1234; |
221 | uint8_t buf1[size1]; |
222 | unique_ptr<IOBuf> iobuf1(IOBuf::wrapBuffer(buf1, size1)); |
223 | EXPECT_EQ(buf1, iobuf1->data()); |
224 | EXPECT_EQ(size1, iobuf1->length()); |
225 | EXPECT_EQ(buf1, iobuf1->buffer()); |
226 | EXPECT_EQ(size1, iobuf1->capacity()); |
227 | |
228 | uint32_t size2 = 0x1234; |
229 | unique_ptr<uint8_t[]> buf2(new uint8_t[size2]); |
230 | unique_ptr<IOBuf> iobuf2(IOBuf::wrapBuffer(buf2.get(), size2)); |
231 | EXPECT_EQ(buf2.get(), iobuf2->data()); |
232 | EXPECT_EQ(size2, iobuf2->length()); |
233 | EXPECT_EQ(buf2.get(), iobuf2->buffer()); |
234 | EXPECT_EQ(size2, iobuf2->capacity()); |
235 | |
236 | uint32_t size3 = 4321; |
237 | unique_ptr<uint8_t[]> buf3(new uint8_t[size3]); |
238 | IOBuf iobuf3(IOBuf::WRAP_BUFFER, buf3.get(), size3); |
239 | EXPECT_EQ(buf3.get(), iobuf3.data()); |
240 | EXPECT_EQ(size3, iobuf3.length()); |
241 | EXPECT_EQ(buf3.get(), iobuf3.buffer()); |
242 | EXPECT_EQ(size3, iobuf3.capacity()); |
243 | |
244 | const uint32_t size4 = 2345; |
245 | unique_ptr<uint8_t[]> buf4(new uint8_t[size4]); |
246 | IOBuf iobuf4 = IOBuf::wrapBufferAsValue(buf4.get(), size4); |
247 | EXPECT_EQ(buf4.get(), iobuf4.data()); |
248 | EXPECT_EQ(size4, iobuf4.length()); |
249 | EXPECT_EQ(buf4.get(), iobuf4.buffer()); |
250 | EXPECT_EQ(size4, iobuf4.capacity()); |
251 | } |
252 | |
253 | TEST(IOBuf, CreateCombined) { |
254 | // Create a combined IOBuf, then destroy it. |
255 | // The data buffer and IOBuf both become unused as part of the destruction |
256 | { |
257 | auto buf = IOBuf::createCombined(256); |
258 | EXPECT_FALSE(buf->isShared()); |
259 | } |
260 | |
261 | // Create a combined IOBuf, clone from it, and then destroy the original |
262 | // IOBuf. The data buffer cannot be deleted until the clone is also |
263 | // destroyed. |
264 | { |
265 | auto bufA = IOBuf::createCombined(256); |
266 | EXPECT_FALSE(bufA->isShared()); |
267 | auto bufB = bufA->clone(); |
268 | EXPECT_TRUE(bufA->isShared()); |
269 | EXPECT_TRUE(bufB->isShared()); |
270 | bufA.reset(); |
271 | EXPECT_FALSE(bufB->isShared()); |
272 | } |
273 | |
274 | // Create a combined IOBuf, then call reserve() to get a larger buffer. |
275 | // The IOBuf no longer points to the combined data buffer, but the |
276 | // overall memory segment cannot be deleted until the IOBuf is also |
277 | // destroyed. |
278 | { |
279 | auto buf = IOBuf::createCombined(256); |
280 | buf->reserve(0, buf->capacity() + 100); |
281 | } |
282 | |
283 | // Create a combined IOBuf, clone from it, then call unshare() on the original |
284 | // buffer. This creates a situation where bufB is pointing at the combined |
285 | // buffer associated with bufA, but bufA is now using a different buffer. |
286 | auto testSwap = [](bool resetAFirst) { |
287 | auto bufA = IOBuf::createCombined(256); |
288 | EXPECT_FALSE(bufA->isShared()); |
289 | auto bufB = bufA->clone(); |
290 | EXPECT_TRUE(bufA->isShared()); |
291 | EXPECT_TRUE(bufB->isShared()); |
292 | bufA->unshare(); |
293 | EXPECT_FALSE(bufA->isShared()); |
294 | EXPECT_FALSE(bufB->isShared()); |
295 | |
296 | if (resetAFirst) { |
297 | bufA.reset(); |
298 | bufB.reset(); |
299 | } else { |
300 | bufB.reset(); |
301 | bufA.reset(); |
302 | } |
303 | }; |
304 | testSwap(true); |
305 | testSwap(false); |
306 | } |
307 | |
308 | void fillBuf(uint8_t* buf, uint32_t length, boost::mt19937& gen) { |
309 | for (uint32_t n = 0; n < length; ++n) { |
310 | buf[n] = static_cast<uint8_t>(gen() & 0xff); |
311 | } |
312 | } |
313 | |
314 | void fillBuf(IOBuf* buf, boost::mt19937& gen) { |
315 | buf->unshare(); |
316 | fillBuf(buf->writableData(), buf->length(), gen); |
317 | } |
318 | |
319 | void checkBuf(const uint8_t* buf, uint32_t length, boost::mt19937& gen) { |
320 | // Rather than using EXPECT_EQ() to check each character, |
321 | // count the number of differences and the first character that differs. |
322 | // This way on error we'll report just that information, rather than tons of |
323 | // failed checks for each byte in the buffer. |
324 | uint32_t numDifferences = 0; |
325 | uint32_t firstDiffIndex = 0; |
326 | uint8_t firstDiffExpected = 0; |
327 | for (uint32_t n = 0; n < length; ++n) { |
328 | uint8_t expected = static_cast<uint8_t>(gen() & 0xff); |
329 | if (buf[n] == expected) { |
330 | continue; |
331 | } |
332 | |
333 | if (numDifferences == 0) { |
334 | firstDiffIndex = n; |
335 | firstDiffExpected = expected; |
336 | } |
337 | ++numDifferences; |
338 | } |
339 | |
340 | EXPECT_EQ(0, numDifferences); |
341 | if (numDifferences > 0) { |
342 | // Cast to int so it will be printed numerically |
343 | // rather than as a char if the check fails |
344 | EXPECT_EQ( |
345 | static_cast<int>(buf[firstDiffIndex]), |
346 | static_cast<int>(firstDiffExpected)); |
347 | } |
348 | } |
349 | |
350 | void checkBuf(IOBuf* buf, boost::mt19937& gen) { |
351 | checkBuf(buf->data(), buf->length(), gen); |
352 | } |
353 | |
354 | void checkBuf(ByteRange buf, boost::mt19937& gen) { |
355 | checkBuf(buf.data(), buf.size(), gen); |
356 | } |
357 | |
358 | void checkChain(IOBuf* buf, boost::mt19937& gen) { |
359 | IOBuf* current = buf; |
360 | do { |
361 | checkBuf(current->data(), current->length(), gen); |
362 | current = current->next(); |
363 | } while (current != buf); |
364 | } |
365 | |
366 | TEST(IOBuf, Chaining) { |
367 | uint32_t fillSeed = 0x12345678; |
368 | boost::mt19937 gen(fillSeed); |
369 | |
370 | // An IOBuf with external storage |
371 | uint32_t headroom = 123; |
372 | unique_ptr<IOBuf> iob1(IOBuf::create(2048)); |
373 | iob1->advance(headroom); |
374 | iob1->append(1500); |
375 | fillBuf(iob1.get(), gen); |
376 | |
377 | // An IOBuf with internal storage |
378 | unique_ptr<IOBuf> iob2(IOBuf::create(20)); |
379 | iob2->append(20); |
380 | fillBuf(iob2.get(), gen); |
381 | |
382 | // An IOBuf around a buffer it doesn't own |
383 | uint8_t localbuf[1234]; |
384 | fillBuf(localbuf, 1234, gen); |
385 | unique_ptr<IOBuf> iob3(IOBuf::wrapBuffer(localbuf, sizeof(localbuf))); |
386 | |
387 | // An IOBuf taking ownership of a user-supplied buffer |
388 | uint32_t heapBufSize = 900; |
389 | uint8_t* heapBuf = static_cast<uint8_t*>(malloc(heapBufSize)); |
390 | fillBuf(heapBuf, heapBufSize, gen); |
391 | unique_ptr<IOBuf> iob4(IOBuf::takeOwnership(heapBuf, heapBufSize)); |
392 | |
393 | // An IOBuf taking ownership of a user-supplied buffer with |
394 | // a custom free function |
395 | uint32_t arrayBufSize = 321; |
396 | uint8_t* arrayBuf = new uint8_t[arrayBufSize]; |
397 | fillBuf(arrayBuf, arrayBufSize, gen); |
398 | uint32_t arrayBufFreeCount = 0; |
399 | unique_ptr<IOBuf> iob5(IOBuf::takeOwnership( |
400 | arrayBuf, arrayBufSize, deleteArrayBuffer, &arrayBufFreeCount)); |
401 | |
402 | EXPECT_FALSE(iob1->isChained()); |
403 | EXPECT_FALSE(iob2->isChained()); |
404 | EXPECT_FALSE(iob3->isChained()); |
405 | EXPECT_FALSE(iob4->isChained()); |
406 | EXPECT_FALSE(iob5->isChained()); |
407 | |
408 | EXPECT_FALSE(iob1->isSharedOne()); |
409 | EXPECT_FALSE(iob2->isSharedOne()); |
410 | EXPECT_TRUE(iob3->isSharedOne()); // since we own the buffer |
411 | EXPECT_FALSE(iob4->isSharedOne()); |
412 | EXPECT_FALSE(iob5->isSharedOne()); |
413 | |
414 | // Chain the buffers all together |
415 | // Since we are going to relinquish ownership of iob2-5 to the chain, |
416 | // store raw pointers to them so we can reference them later. |
417 | IOBuf* iob2ptr = iob2.get(); |
418 | IOBuf* iob3ptr = iob3.get(); |
419 | IOBuf* iob4ptr = iob4.get(); |
420 | IOBuf* iob5ptr = iob5.get(); |
421 | |
422 | iob1->prependChain(std::move(iob2)); |
423 | iob1->prependChain(std::move(iob4)); |
424 | iob2ptr->appendChain(std::move(iob3)); |
425 | iob1->prependChain(std::move(iob5)); |
426 | |
427 | EXPECT_EQ(iob2ptr, iob1->next()); |
428 | EXPECT_EQ(iob3ptr, iob2ptr->next()); |
429 | EXPECT_EQ(iob4ptr, iob3ptr->next()); |
430 | EXPECT_EQ(iob5ptr, iob4ptr->next()); |
431 | EXPECT_EQ(iob1.get(), iob5ptr->next()); |
432 | |
433 | EXPECT_EQ(iob5ptr, iob1->prev()); |
434 | EXPECT_EQ(iob1.get(), iob2ptr->prev()); |
435 | EXPECT_EQ(iob2ptr, iob3ptr->prev()); |
436 | EXPECT_EQ(iob3ptr, iob4ptr->prev()); |
437 | EXPECT_EQ(iob4ptr, iob5ptr->prev()); |
438 | |
439 | EXPECT_TRUE(iob1->isChained()); |
440 | EXPECT_TRUE(iob2ptr->isChained()); |
441 | EXPECT_TRUE(iob3ptr->isChained()); |
442 | EXPECT_TRUE(iob4ptr->isChained()); |
443 | EXPECT_TRUE(iob5ptr->isChained()); |
444 | |
445 | std::size_t fullLength = |
446 | (iob1->length() + iob2ptr->length() + iob3ptr->length() + |
447 | iob4ptr->length() + iob5ptr->length()); |
448 | EXPECT_EQ(5, iob1->countChainElements()); |
449 | EXPECT_EQ(fullLength, iob1->computeChainDataLength()); |
450 | |
451 | // Since iob3 is shared, the entire buffer should report itself as shared |
452 | EXPECT_TRUE(iob1->isShared()); |
453 | // Unshare just iob3 |
454 | iob3ptr->unshareOne(); |
455 | EXPECT_FALSE(iob3ptr->isSharedOne()); |
456 | // Now everything in the chain should be unshared. |
457 | // Check on all members of the chain just for good measure |
458 | EXPECT_FALSE(iob1->isShared()); |
459 | EXPECT_FALSE(iob2ptr->isShared()); |
460 | EXPECT_FALSE(iob3ptr->isShared()); |
461 | EXPECT_FALSE(iob4ptr->isShared()); |
462 | EXPECT_FALSE(iob5ptr->isShared()); |
463 | |
464 | // Check iteration |
465 | gen.seed(fillSeed); |
466 | size_t count = 0; |
467 | for (auto buf : *iob1) { |
468 | checkBuf(buf, gen); |
469 | ++count; |
470 | } |
471 | EXPECT_EQ(5, count); |
472 | |
473 | // Clone one of the IOBufs in the chain |
474 | unique_ptr<IOBuf> iob4clone = iob4ptr->cloneOne(); |
475 | gen.seed(fillSeed); |
476 | checkBuf(iob1.get(), gen); |
477 | checkBuf(iob2ptr, gen); |
478 | checkBuf(iob3ptr, gen); |
479 | checkBuf(iob4clone.get(), gen); |
480 | checkBuf(iob5ptr, gen); |
481 | |
482 | EXPECT_TRUE(iob1->isShared()); |
483 | EXPECT_TRUE(iob2ptr->isShared()); |
484 | EXPECT_TRUE(iob3ptr->isShared()); |
485 | EXPECT_TRUE(iob4ptr->isShared()); |
486 | EXPECT_TRUE(iob5ptr->isShared()); |
487 | |
488 | EXPECT_FALSE(iob1->isSharedOne()); |
489 | EXPECT_FALSE(iob2ptr->isSharedOne()); |
490 | EXPECT_FALSE(iob3ptr->isSharedOne()); |
491 | EXPECT_TRUE(iob4ptr->isSharedOne()); |
492 | EXPECT_FALSE(iob5ptr->isSharedOne()); |
493 | |
494 | // Unshare that clone |
495 | EXPECT_TRUE(iob4clone->isSharedOne()); |
496 | iob4clone->unshare(); |
497 | EXPECT_FALSE(iob4clone->isSharedOne()); |
498 | EXPECT_FALSE(iob4ptr->isSharedOne()); |
499 | EXPECT_FALSE(iob1->isShared()); |
500 | iob4clone.reset(); |
501 | |
502 | // Create a clone of a different IOBuf |
503 | EXPECT_FALSE(iob1->isShared()); |
504 | EXPECT_FALSE(iob3ptr->isSharedOne()); |
505 | |
506 | unique_ptr<IOBuf> iob3clone = iob3ptr->cloneOne(); |
507 | gen.seed(fillSeed); |
508 | checkBuf(iob1.get(), gen); |
509 | checkBuf(iob2ptr, gen); |
510 | checkBuf(iob3clone.get(), gen); |
511 | checkBuf(iob4ptr, gen); |
512 | checkBuf(iob5ptr, gen); |
513 | |
514 | EXPECT_TRUE(iob1->isShared()); |
515 | EXPECT_TRUE(iob3ptr->isSharedOne()); |
516 | EXPECT_FALSE(iob1->isSharedOne()); |
517 | |
518 | // Delete the clone and make sure the original is unshared |
519 | iob3clone.reset(); |
520 | EXPECT_FALSE(iob1->isShared()); |
521 | EXPECT_FALSE(iob3ptr->isSharedOne()); |
522 | |
523 | // Clone the entire chain |
524 | unique_ptr<IOBuf> chainClone = iob1->clone(); |
525 | // Verify that the data is correct. |
526 | EXPECT_EQ(fullLength, chainClone->computeChainDataLength()); |
527 | gen.seed(fillSeed); |
528 | checkChain(chainClone.get(), gen); |
529 | |
530 | // Check that the buffers report sharing correctly |
531 | EXPECT_TRUE(chainClone->isShared()); |
532 | EXPECT_TRUE(iob1->isShared()); |
533 | |
534 | EXPECT_TRUE(iob1->isSharedOne()); |
535 | EXPECT_TRUE(iob2ptr->isSharedOne()); |
536 | EXPECT_TRUE(iob3ptr->isSharedOne()); |
537 | EXPECT_TRUE(iob4ptr->isSharedOne()); |
538 | EXPECT_TRUE(iob5ptr->isSharedOne()); |
539 | |
540 | // Unshare the cloned chain |
541 | chainClone->unshare(); |
542 | EXPECT_FALSE(chainClone->isShared()); |
543 | EXPECT_FALSE(iob1->isShared()); |
544 | |
545 | // Make sure the unshared result still has the same data |
546 | EXPECT_EQ(fullLength, chainClone->computeChainDataLength()); |
547 | gen.seed(fillSeed); |
548 | checkChain(chainClone.get(), gen); |
549 | |
550 | // Destroy this chain |
551 | chainClone.reset(); |
552 | |
553 | // Clone a new chain |
554 | EXPECT_FALSE(iob1->isShared()); |
555 | chainClone = iob1->clone(); |
556 | EXPECT_TRUE(iob1->isShared()); |
557 | EXPECT_TRUE(chainClone->isShared()); |
558 | |
559 | // Delete the original chain |
560 | iob1.reset(); |
561 | EXPECT_FALSE(chainClone->isShared()); |
562 | |
563 | // Coalesce the chain |
564 | // |
565 | // Coalescing this chain will create a new buffer and release the last |
566 | // refcount on the original buffers we created. Also make sure |
567 | // that arrayBufFreeCount increases to one to indicate that arrayBuf was |
568 | // freed. |
569 | EXPECT_EQ(5, chainClone->countChainElements()); |
570 | EXPECT_EQ(0, arrayBufFreeCount); |
571 | |
572 | // Buffer lengths: 1500 20 1234 900 321 |
573 | // Attempting to gather more data than available should fail |
574 | EXPECT_THROW(chainClone->gather(4000), std::overflow_error); |
575 | // Coalesce the first 3 buffers |
576 | chainClone->gather(1521); |
577 | EXPECT_EQ(3, chainClone->countChainElements()); |
578 | EXPECT_EQ(0, arrayBufFreeCount); |
579 | |
580 | // Make sure the data is still the same after coalescing |
581 | EXPECT_EQ(fullLength, chainClone->computeChainDataLength()); |
582 | gen.seed(fillSeed); |
583 | checkChain(chainClone.get(), gen); |
584 | |
585 | // cloneCoalesced |
586 | { |
587 | auto chainCloneCoalesced = chainClone->cloneCoalesced(); |
588 | EXPECT_EQ(1, chainCloneCoalesced->countChainElements()); |
589 | EXPECT_EQ(fullLength, chainCloneCoalesced->computeChainDataLength()); |
590 | gen.seed(fillSeed); |
591 | checkChain(chainCloneCoalesced.get(), gen); |
592 | } |
593 | |
594 | // Coalesce the entire chain |
595 | chainClone->coalesce(); |
596 | EXPECT_EQ(1, chainClone->countChainElements()); |
597 | EXPECT_EQ(1, arrayBufFreeCount); |
598 | |
599 | // Make sure the data is still the same after coalescing |
600 | EXPECT_EQ(fullLength, chainClone->computeChainDataLength()); |
601 | gen.seed(fillSeed); |
602 | checkChain(chainClone.get(), gen); |
603 | |
604 | // Make a new chain to test the unlink and pop operations |
605 | iob1 = IOBuf::create(1); |
606 | iob1->append(1); |
607 | IOBuf* iob1ptr = iob1.get(); |
608 | iob2 = IOBuf::create(3); |
609 | iob2->append(3); |
610 | iob2ptr = iob2.get(); |
611 | iob3 = IOBuf::create(5); |
612 | iob3->append(5); |
613 | iob3ptr = iob3.get(); |
614 | iob4 = IOBuf::create(7); |
615 | iob4->append(7); |
616 | iob4ptr = iob4.get(); |
617 | iob1->appendChain(std::move(iob2)); |
618 | iob1->prev()->appendChain(std::move(iob3)); |
619 | iob1->prev()->appendChain(std::move(iob4)); |
620 | EXPECT_EQ(4, iob1->countChainElements()); |
621 | EXPECT_EQ(16, iob1->computeChainDataLength()); |
622 | |
623 | // Unlink from the middle of the chain |
624 | iob3 = iob3ptr->unlink(); |
625 | EXPECT_TRUE(iob3.get() == iob3ptr); |
626 | EXPECT_EQ(3, iob1->countChainElements()); |
627 | EXPECT_EQ(11, iob1->computeChainDataLength()); |
628 | |
629 | // Unlink from the end of the chain |
630 | iob4 = iob1->prev()->unlink(); |
631 | EXPECT_TRUE(iob4.get() == iob4ptr); |
632 | EXPECT_EQ(2, iob1->countChainElements()); |
633 | EXPECT_TRUE(iob1->next() == iob2ptr); |
634 | EXPECT_EQ(4, iob1->computeChainDataLength()); |
635 | |
636 | // Pop from the front of the chain |
637 | iob2 = iob1->pop(); |
638 | EXPECT_TRUE(iob1.get() == iob1ptr); |
639 | EXPECT_EQ(1, iob1->countChainElements()); |
640 | EXPECT_EQ(1, iob1->computeChainDataLength()); |
641 | EXPECT_TRUE(iob2.get() == iob2ptr); |
642 | EXPECT_EQ(1, iob2->countChainElements()); |
643 | EXPECT_EQ(3, iob2->computeChainDataLength()); |
644 | } |
645 | |
646 | void testFreeFn(void* buffer, void* ptr) { |
647 | uint32_t* freeCount = static_cast<uint32_t*>(ptr); |
648 | ; |
649 | delete[] static_cast<uint8_t*>(buffer); |
650 | if (freeCount) { |
651 | ++(*freeCount); |
652 | } |
653 | } |
654 | |
655 | TEST(IOBuf, Reserve) { |
656 | uint32_t fillSeed = 0x23456789; |
657 | boost::mt19937 gen(fillSeed); |
658 | |
659 | // Reserve does nothing if empty and doesn't have to grow the buffer |
660 | { |
661 | gen.seed(fillSeed); |
662 | unique_ptr<IOBuf> iob(IOBuf::create(2000)); |
663 | EXPECT_EQ(0, iob->headroom()); |
664 | const void* p1 = iob->buffer(); |
665 | iob->reserve(5, 15); |
666 | EXPECT_LE(5, iob->headroom()); |
667 | EXPECT_EQ(p1, iob->buffer()); |
668 | } |
669 | |
670 | // Reserve doesn't reallocate if we have enough total room |
671 | { |
672 | gen.seed(fillSeed); |
673 | unique_ptr<IOBuf> iob(IOBuf::create(2000)); |
674 | iob->append(100); |
675 | fillBuf(iob.get(), gen); |
676 | EXPECT_EQ(0, iob->headroom()); |
677 | EXPECT_EQ(100, iob->length()); |
678 | const void* p1 = iob->buffer(); |
679 | const uint8_t* d1 = iob->data(); |
680 | iob->reserve(100, 1800); |
681 | EXPECT_LE(100, iob->headroom()); |
682 | EXPECT_EQ(p1, iob->buffer()); |
683 | EXPECT_EQ(d1 + 100, iob->data()); |
684 | gen.seed(fillSeed); |
685 | checkBuf(iob.get(), gen); |
686 | } |
687 | |
688 | // Reserve reallocates if we don't have enough total room. |
689 | // NOTE that, with jemalloc, we know that this won't reallocate in place |
690 | // as the size is less than jemallocMinInPlaceExpanadable |
691 | { |
692 | gen.seed(fillSeed); |
693 | unique_ptr<IOBuf> iob(IOBuf::create(2000)); |
694 | iob->append(100); |
695 | fillBuf(iob.get(), gen); |
696 | EXPECT_EQ(0, iob->headroom()); |
697 | EXPECT_EQ(100, iob->length()); |
698 | const void* p1 = iob->buffer(); |
699 | iob->reserve(100, 2512); // allocation sizes are multiples of 256 |
700 | EXPECT_LE(100, iob->headroom()); |
701 | if (folly::usingJEMalloc()) { |
702 | EXPECT_NE(p1, iob->buffer()); |
703 | } |
704 | gen.seed(fillSeed); |
705 | checkBuf(iob.get(), gen); |
706 | } |
707 | |
708 | // Test reserve from internal buffer, this used to segfault |
709 | { |
710 | unique_ptr<IOBuf> iob(IOBuf::create(0)); |
711 | iob->reserve(0, 2000); |
712 | EXPECT_EQ(0, iob->headroom()); |
713 | EXPECT_LE(2000, iob->tailroom()); |
714 | } |
715 | |
716 | // Test reserving from a user-allocated buffer. |
717 | { |
718 | uint8_t* buf = static_cast<uint8_t*>(malloc(100)); |
719 | auto iob = IOBuf::takeOwnership(buf, 100); |
720 | iob->reserve(0, 2000); |
721 | EXPECT_EQ(0, iob->headroom()); |
722 | EXPECT_LE(2000, iob->tailroom()); |
723 | } |
724 | |
725 | // Test reserving from a user-allocated with a custom free function. |
726 | { |
727 | uint32_t freeCount{0}; |
728 | uint8_t* buf = new uint8_t[100]; |
729 | auto iob = IOBuf::takeOwnership(buf, 100, testFreeFn, &freeCount); |
730 | iob->reserve(0, 2000); |
731 | EXPECT_EQ(0, iob->headroom()); |
732 | EXPECT_LE(2000, iob->tailroom()); |
733 | EXPECT_EQ(1, freeCount); |
734 | } |
735 | } |
736 | |
737 | TEST(IOBuf, copyBuffer) { |
738 | std::string s("hello" ); |
739 | auto buf = IOBuf::copyBuffer(s.data(), s.size(), 1, 2); |
740 | EXPECT_EQ(1, buf->headroom()); |
741 | EXPECT_EQ( |
742 | s, |
743 | std::string(reinterpret_cast<const char*>(buf->data()), buf->length())); |
744 | EXPECT_LE(2, buf->tailroom()); |
745 | |
746 | buf = IOBuf::copyBuffer(s, 5, 7); |
747 | EXPECT_EQ(5, buf->headroom()); |
748 | EXPECT_EQ( |
749 | s, |
750 | std::string(reinterpret_cast<const char*>(buf->data()), buf->length())); |
751 | EXPECT_LE(7, buf->tailroom()); |
752 | |
753 | std::string empty; |
754 | buf = IOBuf::copyBuffer(empty, 3, 6); |
755 | EXPECT_EQ(3, buf->headroom()); |
756 | EXPECT_EQ(0, buf->length()); |
757 | EXPECT_LE(6, buf->tailroom()); |
758 | |
759 | // A stack-allocated version |
760 | IOBuf stackBuf(IOBuf::COPY_BUFFER, s, 1, 2); |
761 | EXPECT_EQ(1, stackBuf.headroom()); |
762 | EXPECT_EQ( |
763 | s, |
764 | std::string( |
765 | reinterpret_cast<const char*>(stackBuf.data()), stackBuf.length())); |
766 | EXPECT_LE(2, stackBuf.tailroom()); |
767 | } |
768 | |
769 | TEST(IOBuf, maybeCopyBuffer) { |
770 | std::string s("this is a test" ); |
771 | auto buf = IOBuf::maybeCopyBuffer(s, 1, 2); |
772 | EXPECT_EQ(1, buf->headroom()); |
773 | EXPECT_EQ( |
774 | s, |
775 | std::string(reinterpret_cast<const char*>(buf->data()), buf->length())); |
776 | EXPECT_LE(2, buf->tailroom()); |
777 | |
778 | std::string empty; |
779 | buf = IOBuf::maybeCopyBuffer("" , 5, 7); |
780 | EXPECT_EQ(nullptr, buf.get()); |
781 | |
782 | buf = IOBuf::maybeCopyBuffer("" ); |
783 | EXPECT_EQ(nullptr, buf.get()); |
784 | } |
785 | |
786 | TEST(IOBuf, copyEmptyBuffer) { |
787 | auto buf = IOBuf::copyBuffer(nullptr, 0); |
788 | EXPECT_EQ(buf->length(), 0); |
789 | } |
790 | |
791 | namespace { |
792 | |
793 | int customDeleterCount = 0; |
794 | int destructorCount = 0; |
795 | struct OwnershipTestClass { |
796 | explicit OwnershipTestClass(int v = 0) : val(v) {} |
797 | ~OwnershipTestClass() { |
798 | ++destructorCount; |
799 | } |
800 | int val; |
801 | }; |
802 | |
803 | typedef std::function<void(OwnershipTestClass*)> CustomDeleter; |
804 | |
805 | void customDelete(OwnershipTestClass* p) { |
806 | ++customDeleterCount; |
807 | delete p; |
808 | } |
809 | |
810 | void customDeleteArray(OwnershipTestClass* p) { |
811 | ++customDeleterCount; |
812 | delete[] p; |
813 | } |
814 | |
815 | } // namespace |
816 | |
817 | TEST(IOBuf, takeOwnershipUniquePtr) { |
818 | destructorCount = 0; |
819 | { std::unique_ptr<OwnershipTestClass> p(new OwnershipTestClass()); } |
820 | EXPECT_EQ(1, destructorCount); |
821 | |
822 | destructorCount = 0; |
823 | { std::unique_ptr<OwnershipTestClass[]> p(new OwnershipTestClass[2]); } |
824 | EXPECT_EQ(2, destructorCount); |
825 | |
826 | destructorCount = 0; |
827 | { |
828 | std::unique_ptr<OwnershipTestClass> p(new OwnershipTestClass()); |
829 | std::unique_ptr<IOBuf> buf(IOBuf::takeOwnership(std::move(p))); |
830 | EXPECT_EQ(sizeof(OwnershipTestClass), buf->length()); |
831 | EXPECT_EQ(0, destructorCount); |
832 | } |
833 | EXPECT_EQ(1, destructorCount); |
834 | |
835 | destructorCount = 0; |
836 | { |
837 | std::unique_ptr<OwnershipTestClass[]> p(new OwnershipTestClass[2]); |
838 | std::unique_ptr<IOBuf> buf(IOBuf::takeOwnership(std::move(p), 2)); |
839 | EXPECT_EQ(2 * sizeof(OwnershipTestClass), buf->length()); |
840 | EXPECT_EQ(0, destructorCount); |
841 | } |
842 | EXPECT_EQ(2, destructorCount); |
843 | |
844 | customDeleterCount = 0; |
845 | destructorCount = 0; |
846 | { |
847 | std::unique_ptr<OwnershipTestClass, CustomDeleter> p( |
848 | new OwnershipTestClass(), customDelete); |
849 | std::unique_ptr<IOBuf> buf(IOBuf::takeOwnership(std::move(p))); |
850 | EXPECT_EQ(sizeof(OwnershipTestClass), buf->length()); |
851 | EXPECT_EQ(0, destructorCount); |
852 | } |
853 | EXPECT_EQ(1, destructorCount); |
854 | EXPECT_EQ(1, customDeleterCount); |
855 | |
856 | customDeleterCount = 0; |
857 | destructorCount = 0; |
858 | { |
859 | std::unique_ptr<OwnershipTestClass[], CustomDeleter> p( |
860 | new OwnershipTestClass[2], CustomDeleter(customDeleteArray)); |
861 | std::unique_ptr<IOBuf> buf(IOBuf::takeOwnership(std::move(p), 2)); |
862 | EXPECT_EQ(2 * sizeof(OwnershipTestClass), buf->length()); |
863 | EXPECT_EQ(0, destructorCount); |
864 | } |
865 | EXPECT_EQ(2, destructorCount); |
866 | EXPECT_EQ(1, customDeleterCount); |
867 | } |
868 | |
869 | TEST(IOBuf, Alignment) { |
870 | size_t alignment = alignof(std::max_align_t); |
871 | |
872 | std::vector<size_t> sizes{0, 1, 64, 256, 1024, 1 << 10}; |
873 | for (size_t size : sizes) { |
874 | auto buf = IOBuf::create(size); |
875 | uintptr_t p = reinterpret_cast<uintptr_t>(buf->data()); |
876 | EXPECT_EQ(0, p & (alignment - 1)) << "size=" << size; |
877 | } |
878 | } |
879 | |
880 | TEST(TypedIOBuf, Simple) { |
881 | auto buf = IOBuf::create(0); |
882 | TypedIOBuf<std::size_t> typed(buf.get()); |
883 | const std::size_t n = 10000; |
884 | typed.reserve(0, n); |
885 | EXPECT_LE(n, typed.capacity()); |
886 | for (std::size_t i = 0; i < n; i++) { |
887 | *typed.writableTail() = i; |
888 | typed.append(1); |
889 | } |
890 | EXPECT_EQ(n, typed.length()); |
891 | for (std::size_t i = 0; i < n; i++) { |
892 | EXPECT_EQ(i, typed.data()[i]); |
893 | } |
894 | } |
895 | enum BufType { |
896 | CREATE, |
897 | TAKE_OWNERSHIP_MALLOC, |
898 | TAKE_OWNERSHIP_CUSTOM, |
899 | USER_OWNED, |
900 | }; |
901 | |
902 | // chain element size, number of elements in chain, shared |
903 | class MoveToFbStringTest |
904 | : public ::testing::TestWithParam<std::tuple<int, int, bool, BufType>> { |
905 | protected: |
906 | void SetUp() override { |
907 | elementSize_ = std::get<0>(GetParam()); |
908 | elementCount_ = std::get<1>(GetParam()); |
909 | shared_ = std::get<2>(GetParam()); |
910 | type_ = std::get<3>(GetParam()); |
911 | |
912 | buf_ = makeBuf(); |
913 | for (int i = 0; i < elementCount_ - 1; ++i) { |
914 | buf_->prependChain(makeBuf()); |
915 | } |
916 | EXPECT_EQ(elementCount_, buf_->countChainElements()); |
917 | EXPECT_EQ(elementCount_ * elementSize_, buf_->computeChainDataLength()); |
918 | if (shared_) { |
919 | buf2_ = buf_->clone(); |
920 | EXPECT_EQ(elementCount_, buf2_->countChainElements()); |
921 | EXPECT_EQ(elementCount_ * elementSize_, buf2_->computeChainDataLength()); |
922 | } |
923 | } |
924 | |
925 | std::unique_ptr<IOBuf> makeBuf() { |
926 | unique_ptr<IOBuf> buf; |
927 | switch (type_) { |
928 | case CREATE: |
929 | buf = IOBuf::create(elementSize_); |
930 | buf->append(elementSize_); |
931 | break; |
932 | case TAKE_OWNERSHIP_MALLOC: { |
933 | void* data = malloc(elementSize_); |
934 | if (!data) { |
935 | throw std::bad_alloc(); |
936 | } |
937 | buf = IOBuf::takeOwnership(data, elementSize_); |
938 | break; |
939 | } |
940 | case TAKE_OWNERSHIP_CUSTOM: { |
941 | uint8_t* data = new uint8_t[elementSize_]; |
942 | buf = IOBuf::takeOwnership(data, elementSize_, testFreeFn); |
943 | break; |
944 | } |
945 | case USER_OWNED: { |
946 | unique_ptr<uint8_t[]> data(new uint8_t[elementSize_]); |
947 | buf = IOBuf::wrapBuffer(data.get(), elementSize_); |
948 | ownedBuffers_.emplace_back(std::move(data)); |
949 | break; |
950 | } |
951 | default: |
952 | throw std::invalid_argument("unexpected buffer type parameter" ); |
953 | } |
954 | memset(buf->writableData(), 'x', elementSize_); |
955 | return buf; |
956 | } |
957 | |
958 | void check(std::unique_ptr<IOBuf>& buf) { |
959 | fbstring str = buf->moveToFbString(); |
960 | EXPECT_EQ(elementCount_ * elementSize_, str.size()); |
961 | EXPECT_EQ(elementCount_ * elementSize_, strspn(str.c_str(), "x" )); |
962 | EXPECT_EQ(0, buf->length()); |
963 | EXPECT_EQ(1, buf->countChainElements()); |
964 | EXPECT_EQ(0, buf->computeChainDataLength()); |
965 | EXPECT_FALSE(buf->isChained()); |
966 | } |
967 | |
968 | int elementSize_; |
969 | int elementCount_; |
970 | bool shared_; |
971 | BufType type_; |
972 | std::unique_ptr<IOBuf> buf_; |
973 | std::unique_ptr<IOBuf> buf2_; |
974 | std::vector<std::unique_ptr<uint8_t[]>> ownedBuffers_; |
975 | }; |
976 | |
977 | TEST_P(MoveToFbStringTest, Simple) { |
978 | check(buf_); |
979 | if (shared_) { |
980 | check(buf2_); |
981 | } |
982 | } |
983 | |
984 | INSTANTIATE_TEST_CASE_P( |
985 | MoveToFbString, |
986 | MoveToFbStringTest, |
987 | ::testing::Combine( |
988 | ::testing::Values(0, 1, 24, 256, 1 << 10, 1 << 20), // element size |
989 | ::testing::Values(1, 2, 10), // element count |
990 | ::testing::Bool(), // shared |
991 | ::testing::Values( |
992 | CREATE, |
993 | TAKE_OWNERSHIP_MALLOC, |
994 | TAKE_OWNERSHIP_CUSTOM, |
995 | USER_OWNED))); |
996 | |
997 | TEST(IOBuf, getIov) { |
998 | uint32_t fillSeed = 0xdeadbeef; |
999 | boost::mt19937 gen(fillSeed); |
1000 | |
1001 | size_t len = 4096; |
1002 | size_t count = 32; |
1003 | auto buf = IOBuf::create(len + 1); |
1004 | buf->append(rand() % len + 1); |
1005 | fillBuf(buf.get(), gen); |
1006 | |
1007 | for (size_t i = 0; i < count - 1; i++) { |
1008 | auto buf2 = IOBuf::create(len + 1); |
1009 | buf2->append(rand() % len + 1); |
1010 | fillBuf(buf2.get(), gen); |
1011 | buf->prependChain(std::move(buf2)); |
1012 | } |
1013 | EXPECT_EQ(count, buf->countChainElements()); |
1014 | |
1015 | auto iov = buf->getIov(); |
1016 | EXPECT_EQ(count, iov.size()); |
1017 | |
1018 | IOBuf const* p = buf.get(); |
1019 | for (size_t i = 0; i < count; i++, p = p->next()) { |
1020 | EXPECT_EQ(p->data(), iov[i].iov_base); |
1021 | EXPECT_EQ(p->length(), iov[i].iov_len); |
1022 | } |
1023 | |
1024 | // an empty buf should be skipped in the iov. |
1025 | buf->next()->clear(); |
1026 | iov = buf->getIov(); |
1027 | EXPECT_EQ(count - 1, iov.size()); |
1028 | EXPECT_EQ(buf->next()->next()->data(), iov[1].iov_base); |
1029 | |
1030 | // same for the first one being empty |
1031 | buf->clear(); |
1032 | iov = buf->getIov(); |
1033 | EXPECT_EQ(count - 2, iov.size()); |
1034 | EXPECT_EQ(buf->next()->next()->data(), iov[0].iov_base); |
1035 | |
1036 | // and the last one |
1037 | buf->prev()->clear(); |
1038 | iov = buf->getIov(); |
1039 | EXPECT_EQ(count - 3, iov.size()); |
1040 | |
1041 | // test appending to an existing iovec array |
1042 | iov.clear(); |
1043 | const char localBuf[] = "hello" ; |
1044 | iov.push_back({(void*)localBuf, sizeof(localBuf)}); |
1045 | iov.push_back({(void*)localBuf, sizeof(localBuf)}); |
1046 | buf->appendToIov(&iov); |
1047 | EXPECT_EQ(count - 1, iov.size()); |
1048 | EXPECT_EQ(localBuf, iov[0].iov_base); |
1049 | EXPECT_EQ(localBuf, iov[1].iov_base); |
1050 | // The first two IOBufs were cleared, so the next iov entry |
1051 | // should be the third IOBuf in the chain. |
1052 | EXPECT_EQ(buf->next()->next()->data(), iov[2].iov_base); |
1053 | } |
1054 | |
1055 | TEST(IOBuf, wrapIov) { |
1056 | // Test wrapping IOVs |
1057 | constexpr folly::StringPiece hello = "hello" ; |
1058 | constexpr folly::StringPiece world = "world!" ; |
1059 | folly::fbvector<struct iovec> iov; |
1060 | iov.push_back({nullptr, 0}); |
1061 | iov.push_back({(void*)hello.data(), hello.size()}); |
1062 | iov.push_back({(void*)world.data(), world.size()}); |
1063 | auto wrapped = IOBuf::wrapIov(iov.data(), iov.size()); |
1064 | EXPECT_EQ(iov.size() - 1, wrapped->countChainElements()); |
1065 | IOBuf const* w = wrapped.get(); |
1066 | // skip the first iovec, which is empty/null, as it is ignored by |
1067 | // IOBuf::wrapIov |
1068 | for (size_t i = 0; i < wrapped->countChainElements(); ++i, w = w->next()) { |
1069 | EXPECT_EQ(w->data(), iov[i + 1].iov_base); |
1070 | EXPECT_EQ(w->length(), iov[i + 1].iov_len); |
1071 | } |
1072 | } |
1073 | |
1074 | TEST(IOBuf, takeOwnershipIov) { |
1075 | // Test taking IOVs ownership |
1076 | folly::fbvector<folly::StringPiece> words{"hello" , "world!" }; |
1077 | folly::fbvector<struct iovec> iov; |
1078 | iov.push_back({nullptr, 0}); |
1079 | for (size_t i = 0; i < words.size(); i++) { |
1080 | iov.push_back({(void*)strdup(words[i].data()), words[i].size() + 1}); |
1081 | } |
1082 | auto buf = IOBuf::takeOwnershipIov(iov.data(), iov.size()); |
1083 | EXPECT_EQ(iov.size() - 1, buf->countChainElements()); |
1084 | |
1085 | IOBuf const* b = buf.get(); |
1086 | // skip the first iovec, which is empty/null, as it is ignored by |
1087 | // IOBuf::takeIovOwnership |
1088 | for (size_t i = 0; i < buf->countChainElements(); ++i, b = b->next()) { |
1089 | EXPECT_EQ(words[i], static_cast<const char*>(iov[i + 1].iov_base)); |
1090 | } |
1091 | } |
1092 | |
1093 | TEST(IOBuf, wrapZeroLenIov) { |
1094 | folly::fbvector<struct iovec> iov; |
1095 | iov.push_back({nullptr, 0}); |
1096 | iov.push_back({nullptr, 0}); |
1097 | auto wrapped = IOBuf::wrapIov(iov.data(), iov.size()); |
1098 | EXPECT_NE(nullptr, wrapped); |
1099 | EXPECT_EQ(wrapped->countChainElements(), 1); |
1100 | EXPECT_EQ(wrapped->length(), 0); |
1101 | |
1102 | wrapped = IOBuf::wrapIov(nullptr, 0); |
1103 | EXPECT_NE(nullptr, wrapped); |
1104 | EXPECT_EQ(wrapped->countChainElements(), 1); |
1105 | EXPECT_EQ(wrapped->length(), 0); |
1106 | } |
1107 | |
1108 | TEST(IOBuf, move) { |
1109 | // Default allocate an IOBuf on the stack |
1110 | IOBuf outerBuf; |
1111 | char data[] = "foobar" ; |
1112 | uint32_t length = sizeof(data); |
1113 | uint32_t actualCapacity{0}; |
1114 | const void* ptr{nullptr}; |
1115 | |
1116 | { |
1117 | // Create a small IOBuf on the stack. |
1118 | // Note that IOBufs created on the stack always use an external buffer. |
1119 | IOBuf b1(IOBuf::CREATE, 10); |
1120 | actualCapacity = b1.capacity(); |
1121 | EXPECT_GE(actualCapacity, 10); |
1122 | EXPECT_EQ(0, b1.length()); |
1123 | EXPECT_FALSE(b1.isShared()); |
1124 | ptr = b1.data(); |
1125 | ASSERT_TRUE(ptr != nullptr); |
1126 | memcpy(b1.writableTail(), data, length); |
1127 | b1.append(length); |
1128 | EXPECT_EQ(length, b1.length()); |
1129 | |
1130 | // Use the move constructor |
1131 | IOBuf b2(std::move(b1)); |
1132 | EXPECT_EQ(ptr, b2.data()); |
1133 | EXPECT_EQ(length, b2.length()); |
1134 | EXPECT_EQ(actualCapacity, b2.capacity()); |
1135 | EXPECT_FALSE(b2.isShared()); |
1136 | |
1137 | // Use the move assignment operator |
1138 | outerBuf = std::move(b2); |
1139 | // Close scope, destroying b1 and b2 |
1140 | // (which are both be invalid now anyway after moving out of them) |
1141 | } |
1142 | |
1143 | EXPECT_EQ(ptr, outerBuf.data()); |
1144 | EXPECT_EQ(length, outerBuf.length()); |
1145 | EXPECT_EQ(actualCapacity, outerBuf.capacity()); |
1146 | EXPECT_FALSE(outerBuf.isShared()); |
1147 | } |
1148 | |
1149 | namespace { |
1150 | std::unique_ptr<IOBuf> fromStr(StringPiece sp) { |
1151 | return IOBuf::copyBuffer(ByteRange(sp)); |
1152 | } |
1153 | |
1154 | std::unique_ptr<IOBuf> seq(std::initializer_list<StringPiece> sps) { |
1155 | auto ret = IOBuf::create(0); |
1156 | for (auto sp : sps) { |
1157 | ret->prependChain(IOBuf::copyBuffer(ByteRange(sp))); |
1158 | } |
1159 | return ret; |
1160 | } |
1161 | } // namespace |
1162 | |
1163 | TEST(IOBuf, HashAndEqual) { |
1164 | folly::IOBufEqualTo eq; |
1165 | folly::IOBufHash hash; |
1166 | |
1167 | EXPECT_TRUE(eq(nullptr, nullptr)); |
1168 | EXPECT_EQ(0, hash(nullptr)); |
1169 | |
1170 | auto empty = IOBuf::create(0); |
1171 | |
1172 | EXPECT_TRUE(eq(*empty, *empty)); |
1173 | EXPECT_TRUE(eq(empty, empty)); |
1174 | EXPECT_TRUE(eq(empty.get(), empty.get())); |
1175 | |
1176 | EXPECT_FALSE(eq(nullptr, empty)); |
1177 | EXPECT_FALSE(eq(empty, nullptr)); |
1178 | EXPECT_FALSE(eq(empty.get(), nullptr)); |
1179 | |
1180 | EXPECT_EQ(hash(*empty), hash(empty)); |
1181 | EXPECT_EQ(hash(*empty), hash(empty.get())); |
1182 | EXPECT_NE(0, hash(empty)); |
1183 | EXPECT_NE(0, hash(empty.get())); |
1184 | |
1185 | auto a = fromStr("hello" ); |
1186 | |
1187 | EXPECT_TRUE(eq(*a, *a)); |
1188 | EXPECT_TRUE(eq(a, a)); |
1189 | EXPECT_TRUE(eq(a.get(), a.get())); |
1190 | |
1191 | EXPECT_FALSE(eq(nullptr, a)); |
1192 | EXPECT_FALSE(eq(a, nullptr)); |
1193 | EXPECT_FALSE(eq(a.get(), nullptr)); |
1194 | |
1195 | EXPECT_EQ(hash(*a), hash(a)); |
1196 | EXPECT_EQ(hash(*a), hash(a.get())); |
1197 | EXPECT_NE(0, hash(a)); |
1198 | EXPECT_NE(0, hash(a.get())); |
1199 | |
1200 | auto b = fromStr("hello" ); |
1201 | |
1202 | EXPECT_TRUE(eq(*a, *b)); |
1203 | EXPECT_TRUE(eq(a, b)); |
1204 | EXPECT_TRUE(eq(a.get(), b.get())); |
1205 | |
1206 | EXPECT_EQ(hash(a), hash(b)); |
1207 | EXPECT_EQ(hash(a.get()), hash(b.get())); |
1208 | |
1209 | auto c = fromStr("hellow" ); |
1210 | |
1211 | EXPECT_FALSE(eq(a, c)); |
1212 | EXPECT_FALSE(eq(a.get(), c.get())); |
1213 | EXPECT_NE(hash(a), hash(c)); |
1214 | EXPECT_NE(hash(a.get()), hash(c.get())); |
1215 | |
1216 | auto d = fromStr("world" ); |
1217 | |
1218 | EXPECT_FALSE(eq(a, d)); |
1219 | EXPECT_FALSE(eq(a.get(), d.get())); |
1220 | EXPECT_NE(hash(a), hash(d)); |
1221 | EXPECT_NE(hash(a.get()), hash(d.get())); |
1222 | |
1223 | auto e = fromStr("helloworld" ); |
1224 | auto f = fromStr("hello" ); |
1225 | f->prependChain(fromStr("wo" )); |
1226 | f->prependChain(fromStr("rld" )); |
1227 | |
1228 | EXPECT_TRUE(eq(e, f)); |
1229 | EXPECT_TRUE(eq(e.get(), f.get())); |
1230 | EXPECT_EQ(hash(e), hash(f)); |
1231 | EXPECT_EQ(hash(e.get()), hash(f.get())); |
1232 | } |
1233 | |
1234 | TEST(IOBuf, IOBufCompare) { |
1235 | folly::IOBufCompare op; |
1236 | auto n = std::unique_ptr<IOBuf>{}; |
1237 | auto e = IOBuf::create(0); |
1238 | auto hello1 = seq({"hello" }); |
1239 | auto hello2 = seq({"hel" , "lo" }); |
1240 | auto hello3 = seq({"he" , "ll" , "o" }); |
1241 | auto hellow = seq({"hellow" }); |
1242 | auto hellox = seq({"hellox" }); |
1243 | |
1244 | EXPECT_EQ(ordering::eq, op(n, n)); |
1245 | EXPECT_EQ(ordering::lt, op(n, e)); |
1246 | EXPECT_EQ(ordering::gt, op(e, n)); |
1247 | EXPECT_EQ(ordering::lt, op(e, hello1)); |
1248 | EXPECT_EQ(ordering::gt, op(hello1, e)); |
1249 | EXPECT_EQ(ordering::eq, op(hello1, hello1)); |
1250 | EXPECT_EQ(ordering::eq, op(hello1, hello2)); |
1251 | EXPECT_EQ(ordering::eq, op(hello1, hello3)); |
1252 | EXPECT_EQ(ordering::lt, op(hello1, hellow)); |
1253 | EXPECT_EQ(ordering::gt, op(hellow, hello1)); |
1254 | EXPECT_EQ(ordering::lt, op(hellow, hellox)); |
1255 | EXPECT_EQ(ordering::gt, op(hellox, hellow)); |
1256 | } |
1257 | |
1258 | // reserveSlow() had a bug when reallocating the buffer in place. It would |
1259 | // preserve old headroom if it's not too much (heuristically) but wouldn't |
1260 | // adjust the requested amount of memory to account for that; the end result |
1261 | // would be that reserve() would return with less tailroom than requested. |
1262 | TEST(IOBuf, ReserveWithHeadroom) { |
1263 | // This is assuming jemalloc, where we know that 4096 and 8192 bytes are |
1264 | // valid (and consecutive) allocation sizes. We're hoping that our |
1265 | // 4096-byte buffer can be expanded in place to 8192 (in practice, this |
1266 | // usually happens). |
1267 | const char data[] = "Lorem ipsum dolor sit amet, consectetur adipiscing elit" ; |
1268 | constexpr size_t reservedSize = 24; // sizeof(SharedInfo) |
1269 | // chosen carefully so that the buffer is exactly 4096 bytes |
1270 | IOBuf buf(IOBuf::CREATE, 4096 - reservedSize); |
1271 | buf.advance(10); |
1272 | memcpy(buf.writableData(), data, sizeof(data)); |
1273 | buf.append(sizeof(data)); |
1274 | EXPECT_EQ(sizeof(data), buf.length()); |
1275 | |
1276 | // Grow the buffer (hopefully in place); this would incorrectly reserve |
1277 | // the 10 bytes of headroom, giving us 10 bytes less than requested. |
1278 | size_t tailroom = 8192 - reservedSize - sizeof(data); |
1279 | buf.reserve(0, tailroom); |
1280 | EXPECT_LE(tailroom, buf.tailroom()); |
1281 | EXPECT_EQ(sizeof(data), buf.length()); |
1282 | EXPECT_EQ(0, memcmp(data, buf.data(), sizeof(data))); |
1283 | } |
1284 | |
1285 | TEST(IOBuf, CopyConstructorAndAssignmentOperator) { |
1286 | auto buf = IOBuf::create(4096); |
1287 | append(buf, "hello world" ); |
1288 | auto buf2 = IOBuf::create(4096); |
1289 | append(buf2, " goodbye" ); |
1290 | buf->prependChain(std::move(buf2)); |
1291 | EXPECT_FALSE(buf->isShared()); |
1292 | |
1293 | { |
1294 | auto copy = *buf; |
1295 | EXPECT_TRUE(buf->isShared()); |
1296 | EXPECT_TRUE(copy.isShared()); |
1297 | EXPECT_EQ((void*)buf->data(), (void*)copy.data()); |
1298 | EXPECT_NE(buf->next(), copy.next()); // actually different buffers |
1299 | |
1300 | auto copy2 = *buf; |
1301 | copy2.coalesce(); |
1302 | EXPECT_TRUE(buf->isShared()); |
1303 | EXPECT_TRUE(copy.isShared()); |
1304 | EXPECT_FALSE(copy2.isShared()); |
1305 | |
1306 | auto p = reinterpret_cast<const char*>(copy2.data()); |
1307 | EXPECT_EQ("hello world goodbye" , std::string(p, copy2.length())); |
1308 | } |
1309 | |
1310 | EXPECT_FALSE(buf->isShared()); |
1311 | |
1312 | { |
1313 | folly::IOBuf newBuf(folly::IOBuf::CREATE, 4096); |
1314 | EXPECT_FALSE(newBuf.isShared()); |
1315 | |
1316 | auto newBufCopy = newBuf; |
1317 | EXPECT_TRUE(newBuf.isShared()); |
1318 | EXPECT_TRUE(newBufCopy.isShared()); |
1319 | |
1320 | newBufCopy = *buf; |
1321 | EXPECT_TRUE(buf->isShared()); |
1322 | EXPECT_FALSE(newBuf.isShared()); |
1323 | EXPECT_TRUE(newBufCopy.isShared()); |
1324 | } |
1325 | |
1326 | EXPECT_FALSE(buf->isShared()); |
1327 | } |
1328 | |
1329 | TEST(IOBuf, CloneAsValue) { |
1330 | auto buf = IOBuf::create(4096); |
1331 | append(buf, "hello world" ); |
1332 | { |
1333 | auto buf2 = IOBuf::create(4096); |
1334 | append(buf2, " goodbye" ); |
1335 | buf->prependChain(std::move(buf2)); |
1336 | EXPECT_FALSE(buf->isShared()); |
1337 | } |
1338 | |
1339 | { |
1340 | auto copy = buf->cloneOneAsValue(); |
1341 | EXPECT_TRUE(buf->isShared()); |
1342 | EXPECT_TRUE(copy.isShared()); |
1343 | EXPECT_EQ((void*)buf->data(), (void*)copy.data()); |
1344 | EXPECT_TRUE(buf->isChained()); |
1345 | EXPECT_FALSE(copy.isChained()); |
1346 | |
1347 | auto copy2 = buf->cloneAsValue(); |
1348 | EXPECT_TRUE(buf->isShared()); |
1349 | EXPECT_TRUE(copy.isShared()); |
1350 | EXPECT_TRUE(copy2.isShared()); |
1351 | EXPECT_TRUE(buf->isChained()); |
1352 | EXPECT_TRUE(copy2.isChained()); |
1353 | |
1354 | copy.unshareOne(); |
1355 | EXPECT_TRUE(buf->isShared()); |
1356 | EXPECT_FALSE(copy.isShared()); |
1357 | EXPECT_NE((void*)buf->data(), (void*)copy.data()); |
1358 | EXPECT_TRUE(copy2.isShared()); |
1359 | |
1360 | auto p = reinterpret_cast<const char*>(copy.data()); |
1361 | EXPECT_EQ("hello world" , std::string(p, copy.length())); |
1362 | |
1363 | copy2.coalesce(); |
1364 | EXPECT_FALSE(buf->isShared()); |
1365 | EXPECT_FALSE(copy.isShared()); |
1366 | EXPECT_FALSE(copy2.isShared()); |
1367 | EXPECT_FALSE(copy2.isChained()); |
1368 | |
1369 | auto p2 = reinterpret_cast<const char*>(copy2.data()); |
1370 | EXPECT_EQ("hello world goodbye" , std::string(p2, copy2.length())); |
1371 | } |
1372 | |
1373 | EXPECT_FALSE(buf->isShared()); |
1374 | } |
1375 | |
1376 | namespace { |
1377 | // Use with string literals only |
1378 | std::unique_ptr<IOBuf> wrap(const char* str) { |
1379 | return IOBuf::wrapBuffer(str, strlen(str)); |
1380 | } |
1381 | |
1382 | std::unique_ptr<IOBuf> copy(const char* str) { |
1383 | // At least 1KiB of tailroom, to ensure an external buffer |
1384 | return IOBuf::copyBuffer(str, strlen(str), 0, 1024); |
1385 | } |
1386 | |
1387 | std::string toString(const folly::IOBuf& buf) { |
1388 | std::string result; |
1389 | result.reserve(buf.computeChainDataLength()); |
1390 | for (auto& b : buf) { |
1391 | result.append(reinterpret_cast<const char*>(b.data()), b.size()); |
1392 | } |
1393 | return result; |
1394 | } |
1395 | |
1396 | char* writableStr(folly::IOBuf& buf) { |
1397 | return reinterpret_cast<char*>(buf.writableData()); |
1398 | } |
1399 | |
1400 | } // namespace |
1401 | |
1402 | TEST(IOBuf, ExternallyShared) { |
1403 | struct Item { |
1404 | Item(const char* src, size_t len) : size(len) { |
1405 | CHECK_LE(len, sizeof(buffer)); |
1406 | memcpy(buffer, src, len); |
1407 | } |
1408 | uint32_t refcount{0}; |
1409 | uint8_t size; |
1410 | char buffer[256]; |
1411 | }; |
1412 | |
1413 | auto hello = "hello" ; |
1414 | struct Item it(hello, strlen(hello)); |
1415 | |
1416 | { |
1417 | auto freeFn = [](void* /* unused */, void* userData) { |
1418 | auto it2 = static_cast<struct Item*>(userData); |
1419 | it2->refcount--; |
1420 | }; |
1421 | it.refcount++; |
1422 | auto buf1 = IOBuf::takeOwnership(it.buffer, it.size, freeFn, &it); |
1423 | EXPECT_TRUE(buf1->isManagedOne()); |
1424 | EXPECT_FALSE(buf1->isSharedOne()); |
1425 | |
1426 | buf1->markExternallyShared(); |
1427 | EXPECT_TRUE(buf1->isSharedOne()); |
1428 | |
1429 | { |
1430 | auto buf2 = buf1->clone(); |
1431 | EXPECT_TRUE(buf2->isManagedOne()); |
1432 | EXPECT_TRUE(buf2->isSharedOne()); |
1433 | EXPECT_EQ(buf1->data(), buf2->data()); |
1434 | EXPECT_EQ(it.refcount, 1); |
1435 | } |
1436 | EXPECT_EQ(it.refcount, 1); |
1437 | } |
1438 | EXPECT_EQ(it.refcount, 0); |
1439 | } |
1440 | |
1441 | TEST(IOBuf, Managed) { |
1442 | auto hello = "hello" ; |
1443 | auto buf1UP = wrap(hello); |
1444 | auto buf1 = buf1UP.get(); |
1445 | EXPECT_FALSE(buf1->isManagedOne()); |
1446 | auto buf2UP = copy("world" ); |
1447 | auto buf2 = buf2UP.get(); |
1448 | EXPECT_TRUE(buf2->isManagedOne()); |
1449 | auto buf3UP = wrap(hello); |
1450 | auto buf3 = buf3UP.get(); |
1451 | auto buf4UP = buf2->clone(); |
1452 | auto buf4 = buf4UP.get(); |
1453 | |
1454 | // buf1 and buf3 share the same memory (but are unmanaged) |
1455 | EXPECT_FALSE(buf1->isManagedOne()); |
1456 | EXPECT_FALSE(buf3->isManagedOne()); |
1457 | EXPECT_TRUE(buf1->isSharedOne()); |
1458 | EXPECT_TRUE(buf3->isSharedOne()); |
1459 | EXPECT_EQ(buf1->data(), buf3->data()); |
1460 | |
1461 | // buf2 and buf4 share the same memory (but are managed) |
1462 | EXPECT_TRUE(buf2->isManagedOne()); |
1463 | EXPECT_TRUE(buf4->isManagedOne()); |
1464 | EXPECT_TRUE(buf2->isSharedOne()); |
1465 | EXPECT_TRUE(buf4->isSharedOne()); |
1466 | EXPECT_EQ(buf2->data(), buf4->data()); |
1467 | |
1468 | buf1->prependChain(std::move(buf2UP)); |
1469 | buf1->prependChain(std::move(buf3UP)); |
1470 | buf1->prependChain(std::move(buf4UP)); |
1471 | |
1472 | EXPECT_EQ("helloworldhelloworld" , toString(*buf1)); |
1473 | EXPECT_FALSE(buf1->isManaged()); |
1474 | |
1475 | buf1->makeManaged(); |
1476 | EXPECT_TRUE(buf1->isManaged()); |
1477 | |
1478 | // buf1 and buf3 are now unshared (because they were unmanaged) |
1479 | EXPECT_TRUE(buf1->isManagedOne()); |
1480 | EXPECT_TRUE(buf3->isManagedOne()); |
1481 | EXPECT_FALSE(buf1->isSharedOne()); |
1482 | EXPECT_FALSE(buf3->isSharedOne()); |
1483 | EXPECT_NE(buf1->data(), buf3->data()); |
1484 | |
1485 | // buf2 and buf4 are still shared |
1486 | EXPECT_TRUE(buf2->isManagedOne()); |
1487 | EXPECT_TRUE(buf4->isManagedOne()); |
1488 | EXPECT_TRUE(buf2->isSharedOne()); |
1489 | EXPECT_TRUE(buf4->isSharedOne()); |
1490 | EXPECT_EQ(buf2->data(), buf4->data()); |
1491 | |
1492 | // And verify that the truth is what we expect: modify a byte in buf1 and |
1493 | // buf2, see that the change from buf1 is *not* reflected in buf3, but the |
1494 | // change from buf2 is reflected in buf4. |
1495 | writableStr(*buf1)[0] = 'j'; |
1496 | writableStr(*buf2)[0] = 'x'; |
1497 | EXPECT_EQ("jelloxorldhelloxorld" , toString(*buf1)); |
1498 | } |
1499 | |
1500 | TEST(IOBuf, CoalesceEmptyBuffers) { |
1501 | auto b1 = IOBuf::takeOwnership(nullptr, 0); |
1502 | auto b2 = fromStr("hello" ); |
1503 | auto b3 = IOBuf::takeOwnership(nullptr, 0); |
1504 | |
1505 | b2->appendChain(std::move(b3)); |
1506 | b1->appendChain(std::move(b2)); |
1507 | |
1508 | auto br = b1->coalesce(); |
1509 | |
1510 | EXPECT_TRUE(ByteRange(StringPiece("hello" )) == br); |
1511 | } |
1512 | |
1513 | TEST(IOBuf, CloneCoalescedChain) { |
1514 | auto b = IOBuf::createChain(1000, 100); |
1515 | b->advance(10); |
1516 | const uint32_t fillSeed = 0x12345678; |
1517 | boost::mt19937 gen(fillSeed); |
1518 | { |
1519 | auto c = b.get(); |
1520 | std::size_t length = c->tailroom(); |
1521 | do { |
1522 | length = std::min(length, c->tailroom()); |
1523 | c->append(length--); |
1524 | fillBuf(c, gen); |
1525 | c = c->next(); |
1526 | } while (c != b.get()); |
1527 | } |
1528 | auto c = b->cloneCoalescedAsValue(); |
1529 | EXPECT_FALSE(c.isChained()); // Not chained |
1530 | EXPECT_FALSE(c.isSharedOne()); // Not shared |
1531 | EXPECT_EQ(b->headroom(), c.headroom()); // Preserves headroom |
1532 | EXPECT_LE(b->prev()->tailroom(), c.tailroom()); // Preserves minimum tailroom |
1533 | EXPECT_EQ(b->computeChainDataLength(), c.length()); // Same length |
1534 | gen.seed(fillSeed); |
1535 | checkBuf(&c, gen); // Same contents |
1536 | } |
1537 | |
1538 | TEST(IOBuf, CloneCoalescedSingle) { |
1539 | auto b = IOBuf::create(1000); |
1540 | b->advance(10); |
1541 | b->append(900); |
1542 | const uint32_t fillSeed = 0x12345678; |
1543 | boost::mt19937 gen(fillSeed); |
1544 | fillBuf(b.get(), gen); |
1545 | |
1546 | auto c = b->cloneCoalesced(); |
1547 | EXPECT_FALSE(c->isChained()); // Not chained |
1548 | EXPECT_TRUE(c->isSharedOne()); // Shared |
1549 | EXPECT_EQ(b->buffer(), c->buffer()); |
1550 | EXPECT_EQ(b->capacity(), c->capacity()); |
1551 | EXPECT_EQ(b->data(), c->data()); |
1552 | EXPECT_EQ(b->length(), c->length()); |
1553 | } |
1554 | |
1555 | TEST(IOBuf, fillIov) { |
1556 | auto buf = IOBuf::create(4096); |
1557 | append(buf, "hello" ); |
1558 | auto buf2 = IOBuf::create(4096); |
1559 | append(buf2, "goodbye" ); |
1560 | auto buf3 = IOBuf::create(4096); |
1561 | append(buf3, "hello again" ); |
1562 | |
1563 | buf2->appendChain(std::move(buf3)); |
1564 | buf->appendChain(std::move(buf2)); |
1565 | |
1566 | constexpr size_t iovCount = 3; |
1567 | struct iovec vec[iovCount]; |
1568 | auto res = buf->fillIov(vec, iovCount); |
1569 | |
1570 | EXPECT_EQ(iovCount, res.numIovecs); |
1571 | EXPECT_EQ(23, res.totalLength); |
1572 | EXPECT_EQ( |
1573 | "hello" , |
1574 | std::string( |
1575 | reinterpret_cast<const char*>(vec[0].iov_base), vec[0].iov_len)); |
1576 | EXPECT_EQ( |
1577 | "goodbye" , |
1578 | std::string( |
1579 | reinterpret_cast<const char*>(vec[1].iov_base), vec[1].iov_len)); |
1580 | EXPECT_EQ( |
1581 | "hello again" , |
1582 | std::string( |
1583 | reinterpret_cast<const char*>(vec[2].iov_base), vec[2].iov_len)); |
1584 | } |
1585 | |
1586 | TEST(IOBuf, fillIov2) { |
1587 | auto buf = IOBuf::create(4096); |
1588 | append(buf, "hello" ); |
1589 | auto buf2 = IOBuf::create(4096); |
1590 | append(buf2, "goodbye" ); |
1591 | auto buf3 = IOBuf::create(4096); |
1592 | append(buf2, "hello again" ); |
1593 | |
1594 | buf2->appendChain(std::move(buf3)); |
1595 | buf->appendChain(std::move(buf2)); |
1596 | |
1597 | constexpr size_t iovCount = 2; |
1598 | struct iovec vec[iovCount]; |
1599 | auto res = buf->fillIov(vec, iovCount); |
1600 | |
1601 | EXPECT_EQ(0, res.numIovecs); |
1602 | EXPECT_EQ(0, res.totalLength); |
1603 | } |
1604 | |