2 * Copyright 2012 Facebook, Inc.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 #include "folly/experimental/io/IOBuf.h"
19 #include <gflags/gflags.h>
20 #include <boost/random.hpp>
21 #include <gtest/gtest.h>
23 #include "folly/Malloc.h"
24 #include "folly/Range.h"
27 using folly::StringPiece;
28 using std::unique_ptr;
30 void append(std::unique_ptr<IOBuf>& buf, StringPiece str) {
31 EXPECT_LE(str.size(), buf->tailroom());
32 memcpy(buf->writableData(), str.data(), str.size());
33 buf->append(str.size());
37 unique_ptr<IOBuf> buf(IOBuf::create(100));
38 uint32_t cap = buf->capacity();
40 EXPECT_EQ(0, buf->headroom());
41 EXPECT_EQ(0, buf->length());
42 EXPECT_EQ(cap, buf->tailroom());
46 EXPECT_EQ(10, buf->headroom());
47 EXPECT_EQ(5, buf->length());
48 EXPECT_EQ(cap - 15, buf->tailroom());
49 const char* p = reinterpret_cast<const char*>(buf->data());
50 EXPECT_EQ("hello", std::string(p, buf->length()));
53 EXPECT_EQ(0, buf->headroom());
54 EXPECT_EQ(0, buf->length());
55 EXPECT_EQ(cap, buf->tailroom());
59 void testAllocSize(uint32_t requestedCapacity) {
60 unique_ptr<IOBuf> iobuf(IOBuf::create(requestedCapacity));
61 EXPECT_GE(iobuf->capacity(), requestedCapacity);
64 TEST(IOBuf, AllocSizes) {
65 // Try with a small allocation size that should fit in the internal buffer
68 // Try with a large allocation size that will require an external buffer.
71 // 220 bytes is currently the cutoff
72 // (It would be nice to use the IOBuf::kMaxInternalDataSize constant,
73 // but it's private and it doesn't seem worth making it public just for this
80 void deleteArrayBuffer(void *buf, void* arg) {
81 uint32_t* deleteCount = static_cast<uint32_t*>(arg);
83 uint8_t* bufPtr = static_cast<uint8_t*>(buf);
87 TEST(IOBuf, TakeOwnership) {
89 uint8_t *buf1 = static_cast<uint8_t*>(malloc(size1));
90 unique_ptr<IOBuf> iobuf1(IOBuf::takeOwnership(buf1, size1));
91 EXPECT_EQ(buf1, iobuf1->data());
92 EXPECT_EQ(size1, iobuf1->length());
93 EXPECT_EQ(buf1, iobuf1->buffer());
94 EXPECT_EQ(size1, iobuf1->capacity());
96 uint32_t deleteCount = 0;
97 uint32_t size2 = 4321;
98 uint8_t *buf2 = new uint8_t[size2];
99 unique_ptr<IOBuf> iobuf2(IOBuf::takeOwnership(buf2, size2,
102 EXPECT_EQ(buf2, iobuf2->data());
103 EXPECT_EQ(size2, iobuf2->length());
104 EXPECT_EQ(buf2, iobuf2->buffer());
105 EXPECT_EQ(size2, iobuf2->capacity());
106 EXPECT_EQ(0, deleteCount);
108 EXPECT_EQ(1, deleteCount);
111 TEST(IOBuf, WrapBuffer) {
112 const uint32_t size1 = 1234;
114 unique_ptr<IOBuf> iobuf1(IOBuf::wrapBuffer(buf1, size1));
115 EXPECT_EQ(buf1, iobuf1->data());
116 EXPECT_EQ(size1, iobuf1->length());
117 EXPECT_EQ(buf1, iobuf1->buffer());
118 EXPECT_EQ(size1, iobuf1->capacity());
120 uint32_t size2 = 0x1234;
121 unique_ptr<uint8_t[]> buf2(new uint8_t[size2]);
122 unique_ptr<IOBuf> iobuf2(IOBuf::wrapBuffer(buf2.get(), size2));
123 EXPECT_EQ(buf2.get(), iobuf2->data());
124 EXPECT_EQ(size2, iobuf2->length());
125 EXPECT_EQ(buf2.get(), iobuf2->buffer());
126 EXPECT_EQ(size2, iobuf2->capacity());
129 void fillBuf(uint8_t* buf, uint32_t length, boost::mt19937& gen) {
130 for (uint32_t n = 0; n < length; ++n) {
131 buf[n] = static_cast<uint8_t>(gen() & 0xff);
135 void fillBuf(IOBuf* buf, boost::mt19937& gen) {
137 fillBuf(buf->writableData(), buf->length(), gen);
140 void checkBuf(const uint8_t* buf, uint32_t length, boost::mt19937& gen) {
141 // Rather than using EXPECT_EQ() to check each character,
142 // count the number of differences and the first character that differs.
143 // This way on error we'll report just that information, rather than tons of
144 // failed checks for each byte in the buffer.
145 uint32_t numDifferences = 0;
146 uint32_t firstDiffIndex = 0;
147 uint8_t firstDiffExpected = 0;
148 for (uint32_t n = 0; n < length; ++n) {
149 uint8_t expected = static_cast<uint8_t>(gen() & 0xff);
150 if (buf[n] == expected) {
154 if (numDifferences == 0) {
156 firstDiffExpected = expected;
161 EXPECT_EQ(0, numDifferences);
162 if (numDifferences > 0) {
163 // Cast to int so it will be printed numerically
164 // rather than as a char if the check fails
165 EXPECT_EQ(static_cast<int>(buf[firstDiffIndex]),
166 static_cast<int>(firstDiffExpected));
170 void checkBuf(IOBuf* buf, boost::mt19937& gen) {
171 checkBuf(buf->data(), buf->length(), gen);
174 void checkChain(IOBuf* buf, boost::mt19937& gen) {
175 IOBuf *current = buf;
177 checkBuf(current->data(), current->length(), gen);
178 current = current->next();
179 } while (current != buf);
182 TEST(IOBuf, Chaining) {
183 uint32_t fillSeed = 0x12345678;
184 boost::mt19937 gen(fillSeed);
186 // An IOBuf with external storage
187 uint32_t headroom = 123;
188 unique_ptr<IOBuf> iob1(IOBuf::create(2048));
189 iob1->advance(headroom);
191 fillBuf(iob1.get(), gen);
193 // An IOBuf with internal storage
194 unique_ptr<IOBuf> iob2(IOBuf::create(20));
196 fillBuf(iob2.get(), gen);
198 // An IOBuf around a buffer it doesn't own
199 uint8_t localbuf[1234];
200 fillBuf(localbuf, 1234, gen);
201 unique_ptr<IOBuf> iob3(IOBuf::wrapBuffer(localbuf, sizeof(localbuf)));
203 // An IOBuf taking ownership of a user-supplied buffer
204 uint32_t heapBufSize = 900;
205 uint8_t* heapBuf = static_cast<uint8_t*>(malloc(heapBufSize));
206 fillBuf(heapBuf, heapBufSize, gen);
207 unique_ptr<IOBuf> iob4(IOBuf::takeOwnership(heapBuf, heapBufSize));
209 // An IOBuf taking ownership of a user-supplied buffer with
210 // a custom free function
211 uint32_t arrayBufSize = 321;
212 uint8_t* arrayBuf = new uint8_t[arrayBufSize];
213 fillBuf(arrayBuf, arrayBufSize, gen);
214 uint32_t arrayBufFreeCount = 0;
215 unique_ptr<IOBuf> iob5(IOBuf::takeOwnership(arrayBuf, arrayBufSize,
217 &arrayBufFreeCount));
219 EXPECT_FALSE(iob1->isChained());
220 EXPECT_FALSE(iob2->isChained());
221 EXPECT_FALSE(iob3->isChained());
222 EXPECT_FALSE(iob4->isChained());
223 EXPECT_FALSE(iob5->isChained());
225 EXPECT_FALSE(iob1->isSharedOne());
226 EXPECT_FALSE(iob2->isSharedOne());
227 EXPECT_TRUE(iob3->isSharedOne()); // since we own the buffer
228 EXPECT_FALSE(iob4->isSharedOne());
229 EXPECT_FALSE(iob5->isSharedOne());
231 // Chain the buffers all together
232 // Since we are going to relinquish ownership of iob2-5 to the chain,
233 // store raw pointers to them so we can reference them later.
234 IOBuf* iob2ptr = iob2.get();
235 IOBuf* iob3ptr = iob3.get();
236 IOBuf* iob4ptr = iob4.get();
237 IOBuf* iob5ptr = iob5.get();
239 iob1->prependChain(std::move(iob2));
240 iob1->prependChain(std::move(iob4));
241 iob2ptr->appendChain(std::move(iob3));
242 iob1->prependChain(std::move(iob5));
244 EXPECT_EQ(iob2ptr, iob1->next());
245 EXPECT_EQ(iob3ptr, iob2ptr->next());
246 EXPECT_EQ(iob4ptr, iob3ptr->next());
247 EXPECT_EQ(iob5ptr, iob4ptr->next());
248 EXPECT_EQ(iob1.get(), iob5ptr->next());
250 EXPECT_EQ(iob5ptr, iob1->prev());
251 EXPECT_EQ(iob1.get(), iob2ptr->prev());
252 EXPECT_EQ(iob2ptr, iob3ptr->prev());
253 EXPECT_EQ(iob3ptr, iob4ptr->prev());
254 EXPECT_EQ(iob4ptr, iob5ptr->prev());
256 EXPECT_TRUE(iob1->isChained());
257 EXPECT_TRUE(iob2ptr->isChained());
258 EXPECT_TRUE(iob3ptr->isChained());
259 EXPECT_TRUE(iob4ptr->isChained());
260 EXPECT_TRUE(iob5ptr->isChained());
262 uint64_t fullLength = (iob1->length() + iob2ptr->length() +
263 iob3ptr->length() + iob4ptr->length() +
265 EXPECT_EQ(5, iob1->countChainElements());
266 EXPECT_EQ(fullLength, iob1->computeChainDataLength());
268 // Since iob3 is shared, the entire buffer should report itself as shared
269 EXPECT_TRUE(iob1->isShared());
271 iob3ptr->unshareOne();
272 EXPECT_FALSE(iob3ptr->isSharedOne());
273 // Now everything in the chain should be unshared.
274 // Check on all members of the chain just for good measure
275 EXPECT_FALSE(iob1->isShared());
276 EXPECT_FALSE(iob2ptr->isShared());
277 EXPECT_FALSE(iob3ptr->isShared());
278 EXPECT_FALSE(iob4ptr->isShared());
279 EXPECT_FALSE(iob5ptr->isShared());
282 // Clone one of the IOBufs in the chain
283 unique_ptr<IOBuf> iob4clone = iob4ptr->cloneOne();
285 checkBuf(iob1.get(), gen);
286 checkBuf(iob2ptr, gen);
287 checkBuf(iob3ptr, gen);
288 checkBuf(iob4clone.get(), gen);
289 checkBuf(iob5ptr, gen);
291 EXPECT_TRUE(iob1->isShared());
292 EXPECT_TRUE(iob2ptr->isShared());
293 EXPECT_TRUE(iob3ptr->isShared());
294 EXPECT_TRUE(iob4ptr->isShared());
295 EXPECT_TRUE(iob5ptr->isShared());
297 EXPECT_FALSE(iob1->isSharedOne());
298 EXPECT_FALSE(iob2ptr->isSharedOne());
299 EXPECT_FALSE(iob3ptr->isSharedOne());
300 EXPECT_TRUE(iob4ptr->isSharedOne());
301 EXPECT_FALSE(iob5ptr->isSharedOne());
303 // Unshare that clone
304 EXPECT_TRUE(iob4clone->isSharedOne());
305 iob4clone->unshare();
306 EXPECT_FALSE(iob4clone->isSharedOne());
307 EXPECT_FALSE(iob4ptr->isSharedOne());
308 EXPECT_FALSE(iob1->isShared());
312 // Create a clone of a different IOBuf
313 EXPECT_FALSE(iob1->isShared());
314 EXPECT_FALSE(iob3ptr->isSharedOne());
316 unique_ptr<IOBuf> iob3clone = iob3ptr->cloneOne();
318 checkBuf(iob1.get(), gen);
319 checkBuf(iob2ptr, gen);
320 checkBuf(iob3clone.get(), gen);
321 checkBuf(iob4ptr, gen);
322 checkBuf(iob5ptr, gen);
324 EXPECT_TRUE(iob1->isShared());
325 EXPECT_TRUE(iob3ptr->isSharedOne());
326 EXPECT_FALSE(iob1->isSharedOne());
328 // Delete the clone and make sure the original is unshared
330 EXPECT_FALSE(iob1->isShared());
331 EXPECT_FALSE(iob3ptr->isSharedOne());
334 // Clone the entire chain
335 unique_ptr<IOBuf> chainClone = iob1->clone();
336 // Verify that the data is correct.
337 EXPECT_EQ(fullLength, chainClone->computeChainDataLength());
339 checkChain(chainClone.get(), gen);
341 // Check that the buffers report sharing correctly
342 EXPECT_TRUE(chainClone->isShared());
343 EXPECT_TRUE(iob1->isShared());
345 EXPECT_TRUE(iob1->isSharedOne());
346 // since iob2 has a small internal buffer, it will never be shared
347 EXPECT_FALSE(iob2ptr->isSharedOne());
348 EXPECT_TRUE(iob3ptr->isSharedOne());
349 EXPECT_TRUE(iob4ptr->isSharedOne());
350 EXPECT_TRUE(iob5ptr->isSharedOne());
352 // Unshare the cloned chain
353 chainClone->unshare();
354 EXPECT_FALSE(chainClone->isShared());
355 EXPECT_FALSE(iob1->isShared());
357 // Make sure the unshared result still has the same data
358 EXPECT_EQ(fullLength, chainClone->computeChainDataLength());
360 checkChain(chainClone.get(), gen);
362 // Destroy this chain
367 EXPECT_FALSE(iob1->isShared());
368 chainClone = iob1->clone();
369 EXPECT_TRUE(iob1->isShared());
370 EXPECT_TRUE(chainClone->isShared());
372 // Delete the original chain
374 EXPECT_FALSE(chainClone->isShared());
376 // Coalesce the chain
378 // Coalescing this chain will create a new buffer and release the last
379 // refcount on the original buffers we created. Also make sure
380 // that arrayBufFreeCount increases to one to indicate that arrayBuf was
382 EXPECT_EQ(5, chainClone->countChainElements());
383 EXPECT_EQ(0, arrayBufFreeCount);
385 // Buffer lengths: 1500 20 1234 900 321
386 // Coalesce the first 3 buffers
387 chainClone->gather(1521);
388 EXPECT_EQ(3, chainClone->countChainElements());
389 EXPECT_EQ(0, arrayBufFreeCount);
391 // Make sure the data is still the same after coalescing
392 EXPECT_EQ(fullLength, chainClone->computeChainDataLength());
394 checkChain(chainClone.get(), gen);
396 // Coalesce the entire chain
397 chainClone->coalesce();
398 EXPECT_EQ(1, chainClone->countChainElements());
399 EXPECT_EQ(1, arrayBufFreeCount);
401 // Make sure the data is still the same after coalescing
402 EXPECT_EQ(fullLength, chainClone->computeChainDataLength());
404 checkChain(chainClone.get(), gen);
406 // Make a new chain to test the unlink and pop operations
407 iob1 = IOBuf::create(1);
409 IOBuf *iob1ptr = iob1.get();
410 iob2 = IOBuf::create(3);
412 iob2ptr = iob2.get();
413 iob3 = IOBuf::create(5);
415 iob3ptr = iob3.get();
416 iob4 = IOBuf::create(7);
418 iob4ptr = iob4.get();
419 iob1->appendChain(std::move(iob2));
420 iob1->prev()->appendChain(std::move(iob3));
421 iob1->prev()->appendChain(std::move(iob4));
422 EXPECT_EQ(4, iob1->countChainElements());
423 EXPECT_EQ(16, iob1->computeChainDataLength());
425 // Unlink from the middle of the chain
426 iob3 = iob3ptr->unlink();
427 EXPECT_TRUE(iob3.get() == iob3ptr);
428 EXPECT_EQ(3, iob1->countChainElements());
429 EXPECT_EQ(11, iob1->computeChainDataLength());
431 // Unlink from the end of the chain
432 iob4 = iob1->prev()->unlink();
433 EXPECT_TRUE(iob4.get() == iob4ptr);
434 EXPECT_EQ(2, iob1->countChainElements());
435 EXPECT_TRUE(iob1->next() == iob2ptr);
436 EXPECT_EQ(4, iob1->computeChainDataLength());
438 // Pop from the front of the chain
440 EXPECT_TRUE(iob1.get() == iob1ptr);
441 EXPECT_EQ(1, iob1->countChainElements());
442 EXPECT_EQ(1, iob1->computeChainDataLength());
443 EXPECT_TRUE(iob2.get() == iob2ptr);
444 EXPECT_EQ(1, iob2->countChainElements());
445 EXPECT_EQ(3, iob2->computeChainDataLength());
448 TEST(IOBuf, Reserve) {
449 uint32_t fillSeed = 0x23456789;
450 boost::mt19937 gen(fillSeed);
452 // Reserve does nothing if empty and doesn't have to grow the buffer
455 unique_ptr<IOBuf> iob(IOBuf::create(2000));
456 EXPECT_EQ(0, iob->headroom());
457 const void* p1 = iob->buffer();
459 EXPECT_LE(5, iob->headroom());
460 EXPECT_EQ(p1, iob->buffer());
463 // Reserve doesn't reallocate if we have enough total room
466 unique_ptr<IOBuf> iob(IOBuf::create(2000));
468 fillBuf(iob.get(), gen);
469 EXPECT_EQ(0, iob->headroom());
470 EXPECT_EQ(100, iob->length());
471 const void* p1 = iob->buffer();
472 const uint8_t* d1 = iob->data();
473 iob->reserve(100, 1800);
474 EXPECT_LE(100, iob->headroom());
475 EXPECT_EQ(p1, iob->buffer());
476 EXPECT_EQ(d1 + 100, iob->data());
478 checkBuf(iob.get(), gen);
481 // Reserve reallocates if we don't have enough total room.
482 // NOTE that, with jemalloc, we know that this won't reallocate in place
483 // as the size is less than jemallocMinInPlaceExpanadable
486 unique_ptr<IOBuf> iob(IOBuf::create(2000));
488 fillBuf(iob.get(), gen);
489 EXPECT_EQ(0, iob->headroom());
490 EXPECT_EQ(100, iob->length());
491 const void* p1 = iob->buffer();
492 const uint8_t* d1 = iob->data();
493 iob->reserve(100, 2512); // allocation sizes are multiples of 256
494 EXPECT_LE(100, iob->headroom());
495 if (folly::usingJEMalloc()) {
496 EXPECT_NE(p1, iob->buffer());
499 checkBuf(iob.get(), gen);
502 // Test reserve from internal buffer, this used to segfault
504 unique_ptr<IOBuf> iob(IOBuf::create(0));
505 iob->reserve(0, 2000);
506 EXPECT_EQ(0, iob->headroom());
507 EXPECT_LE(2000, iob->tailroom());
511 TEST(IOBuf, copyBuffer) {
512 std::string s("hello");
513 auto buf = IOBuf::copyBuffer(s.data(), s.size(), 1, 2);
514 EXPECT_EQ(1, buf->headroom());
515 EXPECT_EQ(s, std::string(reinterpret_cast<const char*>(buf->data()),
517 EXPECT_LE(2, buf->tailroom());
522 int customDeleterCount = 0;
523 int destructorCount = 0;
524 struct OwnershipTestClass {
525 explicit OwnershipTestClass(int v = 0) : val(v) { }
526 ~OwnershipTestClass() {
532 typedef std::function<void(OwnershipTestClass*)> CustomDeleter;
534 void customDelete(OwnershipTestClass* p) {
535 ++customDeleterCount;
539 void customDeleteArray(OwnershipTestClass* p) {
540 ++customDeleterCount;
546 TEST(IOBuf, takeOwnershipUniquePtr) {
549 std::unique_ptr<OwnershipTestClass> p(new OwnershipTestClass());
551 EXPECT_EQ(1, destructorCount);
555 std::unique_ptr<OwnershipTestClass[]> p(new OwnershipTestClass[2]);
557 EXPECT_EQ(2, destructorCount);
561 std::unique_ptr<OwnershipTestClass> p(new OwnershipTestClass());
562 std::unique_ptr<IOBuf> buf(IOBuf::takeOwnership(std::move(p)));
563 EXPECT_EQ(sizeof(OwnershipTestClass), buf->length());
564 EXPECT_EQ(0, destructorCount);
566 EXPECT_EQ(1, destructorCount);
570 std::unique_ptr<OwnershipTestClass[]> p(new OwnershipTestClass[2]);
571 std::unique_ptr<IOBuf> buf(IOBuf::takeOwnership(std::move(p), 2));
572 EXPECT_EQ(2 * sizeof(OwnershipTestClass), buf->length());
573 EXPECT_EQ(0, destructorCount);
575 EXPECT_EQ(2, destructorCount);
577 customDeleterCount = 0;
580 std::unique_ptr<OwnershipTestClass, CustomDeleter>
581 p(new OwnershipTestClass(), customDelete);
582 std::unique_ptr<IOBuf> buf(IOBuf::takeOwnership(std::move(p)));
583 EXPECT_EQ(sizeof(OwnershipTestClass), buf->length());
584 EXPECT_EQ(0, destructorCount);
586 EXPECT_EQ(1, destructorCount);
587 EXPECT_EQ(1, customDeleterCount);
589 customDeleterCount = 0;
592 std::unique_ptr<OwnershipTestClass[], CustomDeleter>
593 p(new OwnershipTestClass[2], customDeleteArray);
594 std::unique_ptr<IOBuf> buf(IOBuf::takeOwnership(std::move(p), 2));
595 EXPECT_EQ(2 * sizeof(OwnershipTestClass), buf->length());
596 EXPECT_EQ(0, destructorCount);
598 EXPECT_EQ(2, destructorCount);
599 EXPECT_EQ(1, customDeleterCount);
602 TEST(IOBuf, Alignment) {
603 // max_align_t doesn't exist in gcc 4.6.2
606 } __attribute__((aligned));
607 size_t alignment = alignof(MaxAlign);
609 std::vector<size_t> sizes {0, 1, 64, 256, 1024, 1 << 10};
610 for (size_t size : sizes) {
611 auto buf = IOBuf::create(size);
612 uintptr_t p = reinterpret_cast<uintptr_t>(buf->data());
613 EXPECT_EQ(0, p & (alignment - 1)) << "size=" << size;
617 int main(int argc, char** argv) {
618 testing::InitGoogleTest(&argc, argv);
619 google::ParseCommandLineFlags(&argc, &argv, true);
621 return RUN_ALL_TESTS();