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
28using folly::ByteRange;
29using folly::fbstring;
30using folly::fbvector;
31using folly::IOBuf;
32using folly::ordering;
33using folly::StringPiece;
34using folly::TypedIOBuf;
35using std::unique_ptr;
36
37void 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
43void 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
49TEST(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
77void testAllocSize(uint32_t requestedCapacity) {
78 unique_ptr<IOBuf> iobuf(IOBuf::create(requestedCapacity));
79 EXPECT_GE(iobuf->capacity(), requestedCapacity);
80}
81
82TEST(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
98void 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
105TEST(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
176TEST(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
203TEST(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
219TEST(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
253TEST(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
308void 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
314void fillBuf(IOBuf* buf, boost::mt19937& gen) {
315 buf->unshare();
316 fillBuf(buf->writableData(), buf->length(), gen);
317}
318
319void 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
350void checkBuf(IOBuf* buf, boost::mt19937& gen) {
351 checkBuf(buf->data(), buf->length(), gen);
352}
353
354void checkBuf(ByteRange buf, boost::mt19937& gen) {
355 checkBuf(buf.data(), buf.size(), gen);
356}
357
358void 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
366TEST(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
646void 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
655TEST(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
737TEST(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
769TEST(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
786TEST(IOBuf, copyEmptyBuffer) {
787 auto buf = IOBuf::copyBuffer(nullptr, 0);
788 EXPECT_EQ(buf->length(), 0);
789}
790
791namespace {
792
793int customDeleterCount = 0;
794int destructorCount = 0;
795struct OwnershipTestClass {
796 explicit OwnershipTestClass(int v = 0) : val(v) {}
797 ~OwnershipTestClass() {
798 ++destructorCount;
799 }
800 int val;
801};
802
803typedef std::function<void(OwnershipTestClass*)> CustomDeleter;
804
805void customDelete(OwnershipTestClass* p) {
806 ++customDeleterCount;
807 delete p;
808}
809
810void customDeleteArray(OwnershipTestClass* p) {
811 ++customDeleterCount;
812 delete[] p;
813}
814
815} // namespace
816
817TEST(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
869TEST(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
880TEST(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}
895enum 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
903class 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
977TEST_P(MoveToFbStringTest, Simple) {
978 check(buf_);
979 if (shared_) {
980 check(buf2_);
981 }
982}
983
984INSTANTIATE_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
997TEST(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
1055TEST(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
1074TEST(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
1093TEST(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
1108TEST(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
1149namespace {
1150std::unique_ptr<IOBuf> fromStr(StringPiece sp) {
1151 return IOBuf::copyBuffer(ByteRange(sp));
1152}
1153
1154std::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
1163TEST(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
1234TEST(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.
1262TEST(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
1285TEST(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
1329TEST(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
1376namespace {
1377// Use with string literals only
1378std::unique_ptr<IOBuf> wrap(const char* str) {
1379 return IOBuf::wrapBuffer(str, strlen(str));
1380}
1381
1382std::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
1387std::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
1396char* writableStr(folly::IOBuf& buf) {
1397 return reinterpret_cast<char*>(buf.writableData());
1398}
1399
1400} // namespace
1401
1402TEST(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
1441TEST(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
1500TEST(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
1513TEST(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
1538TEST(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
1555TEST(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
1586TEST(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