Properly align internal buffer in IOBuf.
[folly.git] / folly / experimental / io / test / IOBufTest.cpp
1 /*
2  * Copyright 2012 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/experimental/io/IOBuf.h"
18
19 #include <gflags/gflags.h>
20 #include <boost/random.hpp>
21 #include <gtest/gtest.h>
22
23 #include "folly/Malloc.h"
24 #include "folly/Range.h"
25
26 using folly::IOBuf;
27 using folly::StringPiece;
28 using std::unique_ptr;
29
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());
34 }
35
36 TEST(IOBuf, Simple) {
37   unique_ptr<IOBuf> buf(IOBuf::create(100));
38   uint32_t cap = buf->capacity();
39   EXPECT_LE(100, cap);
40   EXPECT_EQ(0, buf->headroom());
41   EXPECT_EQ(0, buf->length());
42   EXPECT_EQ(cap, buf->tailroom());
43
44   append(buf, "hello");
45   buf->advance(10);
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()));
51
52   buf->clear();
53   EXPECT_EQ(0, buf->headroom());
54   EXPECT_EQ(0, buf->length());
55   EXPECT_EQ(cap, buf->tailroom());
56 }
57
58
59 void testAllocSize(uint32_t requestedCapacity) {
60   unique_ptr<IOBuf> iobuf(IOBuf::create(requestedCapacity));
61   EXPECT_GE(iobuf->capacity(), requestedCapacity);
62 }
63
64 TEST(IOBuf, AllocSizes) {
65   // Try with a small allocation size that should fit in the internal buffer
66   testAllocSize(28);
67
68   // Try with a large allocation size that will require an external buffer.
69   testAllocSize(9000);
70
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
74   // test code.)
75   testAllocSize(220);
76   testAllocSize(219);
77   testAllocSize(221);
78 }
79
80 void deleteArrayBuffer(void *buf, void* arg) {
81   uint32_t* deleteCount = static_cast<uint32_t*>(arg);
82   ++(*deleteCount);
83   uint8_t* bufPtr = static_cast<uint8_t*>(buf);
84   delete[] bufPtr;
85 }
86
87 TEST(IOBuf, TakeOwnership) {
88   uint32_t size1 = 99;
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());
95
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,
100                                                 deleteArrayBuffer,
101                                                 &deleteCount));
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);
107   iobuf2.reset();
108   EXPECT_EQ(1, deleteCount);
109 }
110
111 TEST(IOBuf, WrapBuffer) {
112   const uint32_t size1 = 1234;
113   uint8_t buf1[size1];
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());
119
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());
127 }
128
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);
132   }
133 }
134
135 void fillBuf(IOBuf* buf, boost::mt19937& gen) {
136   buf->unshare();
137   fillBuf(buf->writableData(), buf->length(), gen);
138 }
139
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) {
151       continue;
152     }
153
154     if (numDifferences == 0) {
155       firstDiffIndex = n;
156       firstDiffExpected = expected;
157     }
158     ++numDifferences;
159   }
160
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));
167   }
168 }
169
170 void checkBuf(IOBuf* buf, boost::mt19937& gen) {
171   checkBuf(buf->data(), buf->length(), gen);
172 }
173
174 void checkChain(IOBuf* buf, boost::mt19937& gen) {
175   IOBuf *current = buf;
176   do {
177     checkBuf(current->data(), current->length(), gen);
178     current = current->next();
179   } while (current != buf);
180 }
181
182 TEST(IOBuf, Chaining) {
183   uint32_t fillSeed = 0x12345678;
184   boost::mt19937 gen(fillSeed);
185
186   // An IOBuf with external storage
187   uint32_t headroom = 123;
188   unique_ptr<IOBuf> iob1(IOBuf::create(2048));
189   iob1->advance(headroom);
190   iob1->append(1500);
191   fillBuf(iob1.get(), gen);
192
193   // An IOBuf with internal storage
194   unique_ptr<IOBuf> iob2(IOBuf::create(20));
195   iob2->append(20);
196   fillBuf(iob2.get(), gen);
197
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)));
202
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));
208
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,
216                                               deleteArrayBuffer,
217                                               &arrayBufFreeCount));
218
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());
224
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());
230
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();
238
239   iob1->prependChain(std::move(iob2));
240   iob1->prependChain(std::move(iob4));
241   iob2ptr->appendChain(std::move(iob3));
242   iob1->prependChain(std::move(iob5));
243
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());
249
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());
255
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());
261
262   uint64_t fullLength = (iob1->length() + iob2ptr->length() +
263                          iob3ptr->length() + iob4ptr->length() +
264                          iob5ptr->length());
265   EXPECT_EQ(5, iob1->countChainElements());
266   EXPECT_EQ(fullLength, iob1->computeChainDataLength());
267
268   // Since iob3 is shared, the entire buffer should report itself as shared
269   EXPECT_TRUE(iob1->isShared());
270   // Unshare just iob3
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());
280
281
282   // Clone one of the IOBufs in the chain
283   unique_ptr<IOBuf> iob4clone = iob4ptr->cloneOne();
284   gen.seed(fillSeed);
285   checkBuf(iob1.get(), gen);
286   checkBuf(iob2ptr, gen);
287   checkBuf(iob3ptr, gen);
288   checkBuf(iob4clone.get(), gen);
289   checkBuf(iob5ptr, gen);
290
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());
296
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());
302
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());
309   iob4clone.reset();
310
311
312   // Create a clone of a different IOBuf
313   EXPECT_FALSE(iob1->isShared());
314   EXPECT_FALSE(iob3ptr->isSharedOne());
315
316   unique_ptr<IOBuf> iob3clone = iob3ptr->cloneOne();
317   gen.seed(fillSeed);
318   checkBuf(iob1.get(), gen);
319   checkBuf(iob2ptr, gen);
320   checkBuf(iob3clone.get(), gen);
321   checkBuf(iob4ptr, gen);
322   checkBuf(iob5ptr, gen);
323
324   EXPECT_TRUE(iob1->isShared());
325   EXPECT_TRUE(iob3ptr->isSharedOne());
326   EXPECT_FALSE(iob1->isSharedOne());
327
328   // Delete the clone and make sure the original is unshared
329   iob3clone.reset();
330   EXPECT_FALSE(iob1->isShared());
331   EXPECT_FALSE(iob3ptr->isSharedOne());
332
333
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());
338   gen.seed(fillSeed);
339   checkChain(chainClone.get(), gen);
340
341   // Check that the buffers report sharing correctly
342   EXPECT_TRUE(chainClone->isShared());
343   EXPECT_TRUE(iob1->isShared());
344
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());
351
352   // Unshare the cloned chain
353   chainClone->unshare();
354   EXPECT_FALSE(chainClone->isShared());
355   EXPECT_FALSE(iob1->isShared());
356
357   // Make sure the unshared result still has the same data
358   EXPECT_EQ(fullLength, chainClone->computeChainDataLength());
359   gen.seed(fillSeed);
360   checkChain(chainClone.get(), gen);
361
362   // Destroy this chain
363   chainClone.reset();
364
365
366   // Clone a new chain
367   EXPECT_FALSE(iob1->isShared());
368   chainClone = iob1->clone();
369   EXPECT_TRUE(iob1->isShared());
370   EXPECT_TRUE(chainClone->isShared());
371
372   // Delete the original chain
373   iob1.reset();
374   EXPECT_FALSE(chainClone->isShared());
375
376   // Coalesce the chain
377   //
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
381   // freed.
382   EXPECT_EQ(5, chainClone->countChainElements());
383   EXPECT_EQ(0, arrayBufFreeCount);
384
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);
390
391   // Make sure the data is still the same after coalescing
392   EXPECT_EQ(fullLength, chainClone->computeChainDataLength());
393   gen.seed(fillSeed);
394   checkChain(chainClone.get(), gen);
395
396   // Coalesce the entire chain
397   chainClone->coalesce();
398   EXPECT_EQ(1, chainClone->countChainElements());
399   EXPECT_EQ(1, arrayBufFreeCount);
400
401   // Make sure the data is still the same after coalescing
402   EXPECT_EQ(fullLength, chainClone->computeChainDataLength());
403   gen.seed(fillSeed);
404   checkChain(chainClone.get(), gen);
405
406   // Make a new chain to test the unlink and pop operations
407   iob1 = IOBuf::create(1);
408   iob1->append(1);
409   IOBuf *iob1ptr = iob1.get();
410   iob2 = IOBuf::create(3);
411   iob2->append(3);
412   iob2ptr = iob2.get();
413   iob3 = IOBuf::create(5);
414   iob3->append(5);
415   iob3ptr = iob3.get();
416   iob4 = IOBuf::create(7);
417   iob4->append(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());
424
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());
430
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());
437
438   // Pop from the front of the chain
439   iob2 = iob1->pop();
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());
446 }
447
448 TEST(IOBuf, Reserve) {
449   uint32_t fillSeed = 0x23456789;
450   boost::mt19937 gen(fillSeed);
451
452   // Reserve does nothing if empty and doesn't have to grow the buffer
453   {
454     gen.seed(fillSeed);
455     unique_ptr<IOBuf> iob(IOBuf::create(2000));
456     EXPECT_EQ(0, iob->headroom());
457     const void* p1 = iob->buffer();
458     iob->reserve(5, 15);
459     EXPECT_LE(5, iob->headroom());
460     EXPECT_EQ(p1, iob->buffer());
461   }
462
463   // Reserve doesn't reallocate if we have enough total room
464   {
465     gen.seed(fillSeed);
466     unique_ptr<IOBuf> iob(IOBuf::create(2000));
467     iob->append(100);
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());
477     gen.seed(fillSeed);
478     checkBuf(iob.get(), gen);
479   }
480
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
484   {
485     gen.seed(fillSeed);
486     unique_ptr<IOBuf> iob(IOBuf::create(2000));
487     iob->append(100);
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());
497     }
498     gen.seed(fillSeed);
499     checkBuf(iob.get(), gen);
500   }
501
502   // Test reserve from internal buffer, this used to segfault
503   {
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());
508   }
509 }
510
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()),
516                            buf->length()));
517   EXPECT_LE(2, buf->tailroom());
518 }
519
520 namespace {
521
522 int customDeleterCount = 0;
523 int destructorCount = 0;
524 struct OwnershipTestClass {
525   explicit OwnershipTestClass(int v = 0) : val(v) { }
526   ~OwnershipTestClass() {
527     ++destructorCount;
528   }
529   int val;
530 };
531
532 typedef std::function<void(OwnershipTestClass*)> CustomDeleter;
533
534 void customDelete(OwnershipTestClass* p) {
535   ++customDeleterCount;
536   delete p;
537 }
538
539 void customDeleteArray(OwnershipTestClass* p) {
540   ++customDeleterCount;
541   delete[] p;
542 }
543
544 }  // namespace
545
546 TEST(IOBuf, takeOwnershipUniquePtr) {
547   destructorCount = 0;
548   {
549     std::unique_ptr<OwnershipTestClass> p(new OwnershipTestClass());
550   }
551   EXPECT_EQ(1, destructorCount);
552
553   destructorCount = 0;
554   {
555     std::unique_ptr<OwnershipTestClass[]> p(new OwnershipTestClass[2]);
556   }
557   EXPECT_EQ(2, destructorCount);
558
559   destructorCount = 0;
560   {
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);
565   }
566   EXPECT_EQ(1, destructorCount);
567
568   destructorCount = 0;
569   {
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);
574   }
575   EXPECT_EQ(2, destructorCount);
576
577   customDeleterCount = 0;
578   destructorCount = 0;
579   {
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);
585   }
586   EXPECT_EQ(1, destructorCount);
587   EXPECT_EQ(1, customDeleterCount);
588
589   customDeleterCount = 0;
590   destructorCount = 0;
591   {
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);
597   }
598   EXPECT_EQ(2, destructorCount);
599   EXPECT_EQ(1, customDeleterCount);
600 }
601
602 TEST(IOBuf, Alignment) {
603   // max_align_t doesn't exist in gcc 4.6.2
604   struct MaxAlign {
605     char c;
606   } __attribute__((aligned));
607   size_t alignment = alignof(MaxAlign);
608
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;
614   }
615 }
616
617 int main(int argc, char** argv) {
618   testing::InitGoogleTest(&argc, argv);
619   google::ParseCommandLineFlags(&argc, &argv, true);
620
621   return RUN_ALL_TESTS();
622 }