readFile reads an entire file into a string, vector<char>, or similar
[folly.git] / folly / io / test / IOBufCursorTest.cpp
1 /*
2  * Copyright 2014 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
19 #include <gflags/gflags.h>
20 #include <boost/random.hpp>
21 #include <gtest/gtest.h>
22 #include "folly/Benchmark.h"
23 #include "folly/Range.h"
24 #include "folly/io/Cursor.h"
25
26 DECLARE_bool(benchmark);
27
28 using folly::IOBuf;
29 using std::unique_ptr;
30 using namespace folly::io;
31
32 TEST(IOBuf, RWCursor) {
33   unique_ptr<IOBuf> iobuf1(IOBuf::create(20));
34   iobuf1->append(20);
35   unique_ptr<IOBuf> iobuf2(IOBuf::create(20));
36   iobuf2->append(20);
37
38   IOBuf* iob2ptr = iobuf2.get();
39   iobuf1->prependChain(std::move(iobuf2));
40
41   EXPECT_TRUE(iobuf1->isChained());
42
43   RWPrivateCursor wcursor(iobuf1.get());
44   Cursor rcursor(iobuf1.get());
45   wcursor.writeLE((uint64_t)1);
46   wcursor.writeLE((uint64_t)1);
47   wcursor.writeLE((uint64_t)1);
48   wcursor.write((uint8_t)1);
49
50   EXPECT_EQ(1, rcursor.readLE<uint64_t>());
51   rcursor.skip(8);
52   EXPECT_EQ(1, rcursor.readLE<uint32_t>());
53   rcursor.skip(0);
54   EXPECT_EQ(0, rcursor.read<uint8_t>());
55   EXPECT_EQ(0, rcursor.read<uint8_t>());
56   EXPECT_EQ(0, rcursor.read<uint8_t>());
57   EXPECT_EQ(0, rcursor.read<uint8_t>());
58   EXPECT_EQ(1, rcursor.read<uint8_t>());
59 }
60
61 TEST(IOBuf, skip) {
62   unique_ptr<IOBuf> iobuf1(IOBuf::create(20));
63   iobuf1->append(20);
64   RWPrivateCursor wcursor(iobuf1.get());
65   wcursor.write((uint8_t)1);
66   wcursor.write((uint8_t)2);
67   Cursor cursor(iobuf1.get());
68   cursor.skip(1);
69   EXPECT_EQ(2, cursor.read<uint8_t>());
70 }
71
72 TEST(IOBuf, reset) {
73   unique_ptr<IOBuf> iobuf1(IOBuf::create(20));
74   iobuf1->append(20);
75   RWPrivateCursor wcursor(iobuf1.get());
76   wcursor.write((uint8_t)1);
77   wcursor.write((uint8_t)2);
78   wcursor.reset(iobuf1.get());
79   EXPECT_EQ(1, wcursor.read<uint8_t>());
80 }
81
82 TEST(IOBuf, copy_assign_convert) {
83   unique_ptr<IOBuf> iobuf1(IOBuf::create(20));
84   iobuf1->append(20);
85   RWPrivateCursor wcursor(iobuf1.get());
86   RWPrivateCursor cursor2(wcursor);
87   RWPrivateCursor cursor3(iobuf1.get());
88
89   wcursor.write((uint8_t)1);
90   cursor3 = wcursor;
91   wcursor.write((uint8_t)2);
92   Cursor cursor4(wcursor);
93   RWPrivateCursor cursor5(wcursor);
94   wcursor.write((uint8_t)3);
95
96   EXPECT_EQ(1, cursor2.read<uint8_t>());
97   EXPECT_EQ(2, cursor3.read<uint8_t>());
98   EXPECT_EQ(3, cursor4.read<uint8_t>());
99 }
100
101 TEST(IOBuf, overloading) {
102   unique_ptr<IOBuf> iobuf1(IOBuf::create(20));
103   iobuf1->append(20);
104   RWPrivateCursor wcursor(iobuf1.get());
105   wcursor += 1;
106   wcursor.write((uint8_t)1);
107   Cursor cursor(iobuf1.get());
108   cursor += 1;
109   EXPECT_EQ(1, cursor.read<uint8_t>());
110 }
111
112 TEST(IOBuf, endian) {
113   unique_ptr<IOBuf> iobuf1(IOBuf::create(20));
114   iobuf1->append(20);
115   RWPrivateCursor wcursor(iobuf1.get());
116   Cursor rcursor(iobuf1.get());
117   uint16_t v = 1;
118   int16_t vu = -1;
119   wcursor.writeBE(v);
120   wcursor.writeBE(vu);
121   // Try a couple combinations to ensure they were generated correctly
122   wcursor.writeBE(vu);
123   wcursor.writeLE(vu);
124   wcursor.writeLE(vu);
125   wcursor.writeLE(v);
126   EXPECT_EQ(v, rcursor.readBE<uint16_t>());
127 }
128
129 TEST(IOBuf, Cursor) {
130   unique_ptr<IOBuf> iobuf1(IOBuf::create(1));
131   iobuf1->append(1);
132   RWPrivateCursor c(iobuf1.get());
133   c.write((uint8_t)40); // OK
134   try {
135     c.write((uint8_t)10); // Bad write, checked should except.
136     EXPECT_EQ(true, false);
137   } catch (...) {
138   }
139 }
140
141 TEST(IOBuf, UnshareCursor) {
142   uint8_t buf = 0;
143   unique_ptr<IOBuf> iobuf1(IOBuf::wrapBuffer(&buf, 1));
144   unique_ptr<IOBuf> iobuf2(IOBuf::wrapBuffer(&buf, 1));
145   RWUnshareCursor c1(iobuf1.get());
146   RWUnshareCursor c2(iobuf2.get());
147
148   c1.write((uint8_t)10); // This should duplicate the two buffers.
149   uint8_t t = c2.read<uint8_t>();
150   EXPECT_EQ(0, t);
151
152   iobuf1 = IOBuf::wrapBuffer(&buf, 1);
153   iobuf2 = IOBuf::wrapBuffer(&buf, 1);
154   RWPrivateCursor c3(iobuf1.get());
155   RWPrivateCursor c4(iobuf2.get());
156
157   c3.write((uint8_t)10); // This should _not_ duplicate the two buffers.
158   t = c4.read<uint8_t>();
159   EXPECT_EQ(10, t);
160 }
161
162 namespace {
163 void append(std::unique_ptr<IOBuf>& buf, folly::StringPiece data) {
164   EXPECT_LE(data.size(), buf->tailroom());
165   memcpy(buf->writableData(), data.data(), data.size());
166   buf->append(data.size());
167 }
168
169 void append(Appender& appender, folly::StringPiece data) {
170   appender.push(reinterpret_cast<const uint8_t*>(data.data()), data.size());
171 }
172
173 std::string toString(const IOBuf& buf) {
174   std::string str;
175   Cursor cursor(&buf);
176   std::pair<const uint8_t*, size_t> p;
177   while ((p = cursor.peek()).second) {
178     str.append(reinterpret_cast<const char*>(p.first), p.second);
179     cursor.skip(p.second);
180   }
181   return str;
182 }
183
184 }  // namespace
185
186 TEST(IOBuf, PullAndPeek) {
187   std::unique_ptr<IOBuf> iobuf1(IOBuf::create(10));
188   append(iobuf1, "he");
189   std::unique_ptr<IOBuf> iobuf2(IOBuf::create(10));
190   append(iobuf2, "llo ");
191   std::unique_ptr<IOBuf> iobuf3(IOBuf::create(10));
192   append(iobuf3, "world");
193   iobuf1->prependChain(std::move(iobuf2));
194   iobuf1->prependChain(std::move(iobuf3));
195   EXPECT_EQ(3, iobuf1->countChainElements());
196   EXPECT_EQ(11, iobuf1->computeChainDataLength());
197
198   char buf[12];
199   memset(buf, 0, sizeof(buf));
200   Cursor(iobuf1.get()).pull(buf, 11);
201   EXPECT_EQ("hello world", std::string(buf));
202
203   memset(buf, 0, sizeof(buf));
204   EXPECT_EQ(11, Cursor(iobuf1.get()).pullAtMost(buf, 20));
205   EXPECT_EQ("hello world", std::string(buf));
206
207   EXPECT_THROW({Cursor(iobuf1.get()).pull(buf, 20);},
208                std::out_of_range);
209
210   {
211     RWPrivateCursor cursor(iobuf1.get());
212     auto p = cursor.peek();
213     EXPECT_EQ("he", std::string(reinterpret_cast<const char*>(p.first),
214                                 p.second));
215     cursor.skip(p.second);
216     p = cursor.peek();
217     EXPECT_EQ("llo ", std::string(reinterpret_cast<const char*>(p.first),
218                                   p.second));
219     cursor.skip(p.second);
220     p = cursor.peek();
221     EXPECT_EQ("world", std::string(reinterpret_cast<const char*>(p.first),
222                                    p.second));
223     cursor.skip(p.second);
224     EXPECT_EQ(3, iobuf1->countChainElements());
225     EXPECT_EQ(11, iobuf1->computeChainDataLength());
226   }
227
228   {
229     RWPrivateCursor cursor(iobuf1.get());
230     cursor.gather(11);
231     auto p = cursor.peek();
232     EXPECT_EQ("hello world", std::string(reinterpret_cast<const
233                                          char*>(p.first), p.second));
234     EXPECT_EQ(1, iobuf1->countChainElements());
235     EXPECT_EQ(11, iobuf1->computeChainDataLength());
236   }
237 }
238
239 TEST(IOBuf, Gather) {
240   std::unique_ptr<IOBuf> iobuf1(IOBuf::create(10));
241   append(iobuf1, "he");
242   std::unique_ptr<IOBuf> iobuf2(IOBuf::create(10));
243   append(iobuf2, "llo ");
244   std::unique_ptr<IOBuf> iobuf3(IOBuf::create(10));
245   append(iobuf3, "world");
246   iobuf1->prependChain(std::move(iobuf2));
247   iobuf1->prependChain(std::move(iobuf3));
248   EXPECT_EQ(3, iobuf1->countChainElements());
249   EXPECT_EQ(11, iobuf1->computeChainDataLength());
250
251   // Attempting to gather() more data than available in the chain should fail.
252   // Try from the very beginning of the chain.
253   RWPrivateCursor cursor(iobuf1.get());
254   EXPECT_THROW(cursor.gather(15), std::overflow_error);
255   // Now try from the middle of the chain
256   cursor += 3;
257   EXPECT_THROW(cursor.gather(10), std::overflow_error);
258
259   // Calling gatherAtMost() should succeed, however, and just gather
260   // as much as it can
261   cursor.gatherAtMost(10);
262   EXPECT_EQ(8, cursor.length());
263   EXPECT_EQ(8, cursor.totalLength());
264   EXPECT_EQ("lo world",
265             folly::StringPiece(reinterpret_cast<const char*>(cursor.data()),
266                                cursor.length()));
267   EXPECT_EQ(2, iobuf1->countChainElements());
268   EXPECT_EQ(11, iobuf1->computeChainDataLength());
269
270   // Now try gather again on the chain head
271   cursor = RWPrivateCursor(iobuf1.get());
272   cursor.gather(5);
273   // Since gather() doesn't split buffers, everything should be collapsed into
274   // a single buffer now.
275   EXPECT_EQ(1, iobuf1->countChainElements());
276   EXPECT_EQ(11, iobuf1->computeChainDataLength());
277   EXPECT_EQ(11, cursor.length());
278   EXPECT_EQ(11, cursor.totalLength());
279 }
280
281 TEST(IOBuf, cloneAndInsert) {
282   std::unique_ptr<IOBuf> iobuf1(IOBuf::create(10));
283   append(iobuf1, "he");
284   std::unique_ptr<IOBuf> iobuf2(IOBuf::create(10));
285   append(iobuf2, "llo ");
286   std::unique_ptr<IOBuf> iobuf3(IOBuf::create(10));
287   append(iobuf3, "world");
288   iobuf1->prependChain(std::move(iobuf2));
289   iobuf1->prependChain(std::move(iobuf3));
290   EXPECT_EQ(3, iobuf1->countChainElements());
291   EXPECT_EQ(11, iobuf1->computeChainDataLength());
292
293   std::unique_ptr<IOBuf> cloned;
294
295   Cursor(iobuf1.get()).clone(cloned, 3);
296   EXPECT_EQ(2, cloned->countChainElements());
297   EXPECT_EQ(3, cloned->computeChainDataLength());
298
299
300   EXPECT_EQ(11, Cursor(iobuf1.get()).cloneAtMost(cloned, 20));
301   EXPECT_EQ(3, cloned->countChainElements());
302   EXPECT_EQ(11, cloned->computeChainDataLength());
303
304
305   EXPECT_THROW({Cursor(iobuf1.get()).clone(cloned, 20);},
306                std::out_of_range);
307
308   {
309     // Check that inserting in the middle of an iobuf splits
310     RWPrivateCursor cursor(iobuf1.get());
311     Cursor(iobuf1.get()).clone(cloned, 3);
312     EXPECT_EQ(2, cloned->countChainElements());
313     EXPECT_EQ(3, cloned->computeChainDataLength());
314
315     cursor.skip(1);
316
317     cursor.insert(std::move(cloned));
318     cursor.insert(folly::IOBuf::create(0));
319     EXPECT_EQ(7, iobuf1->countChainElements());
320     EXPECT_EQ(14, iobuf1->computeChainDataLength());
321     // Check that nextBuf got set correctly to the buffer with 1 byte left
322     EXPECT_EQ(1, cursor.peek().second);
323     cursor.read<uint8_t>();
324   }
325
326   {
327     // Check that inserting at the end doesn't create empty buf
328     RWPrivateCursor cursor(iobuf1.get());
329     Cursor(iobuf1.get()).clone(cloned, 1);
330     EXPECT_EQ(1, cloned->countChainElements());
331     EXPECT_EQ(1, cloned->computeChainDataLength());
332
333     cursor.skip(1);
334
335     cursor.insert(std::move(cloned));
336     EXPECT_EQ(8, iobuf1->countChainElements());
337     EXPECT_EQ(15, iobuf1->computeChainDataLength());
338     // Check that nextBuf got set correctly
339     cursor.read<uint8_t>();
340   }
341   {
342     // Check that inserting at the beginning doesn't create empty buf
343     RWPrivateCursor cursor(iobuf1.get());
344     Cursor(iobuf1.get()).clone(cloned, 1);
345     EXPECT_EQ(1, cloned->countChainElements());
346     EXPECT_EQ(1, cloned->computeChainDataLength());
347
348     cursor.insert(std::move(cloned));
349     EXPECT_EQ(9, iobuf1->countChainElements());
350     EXPECT_EQ(16, iobuf1->computeChainDataLength());
351     // Check that nextBuf got set correctly
352     cursor.read<uint8_t>();
353   }
354 }
355
356 TEST(IOBuf, Appender) {
357   std::unique_ptr<IOBuf> head(IOBuf::create(10));
358   append(head, "hello");
359
360   Appender app(head.get(), 10);
361   uint32_t cap = head->capacity();
362   uint32_t len1 = app.length();
363   EXPECT_EQ(cap - 5, len1);
364   app.ensure(len1);  // won't grow
365   EXPECT_EQ(len1, app.length());
366   app.ensure(len1 + 1);  // will grow
367   EXPECT_LE(len1 + 1, app.length());
368
369   append(app, " world");
370   EXPECT_EQ("hello world", toString(*head));
371 }
372
373 TEST(IOBuf, QueueAppender) {
374   folly::IOBufQueue queue;
375
376   // Allocate 100 bytes at once, but don't grow past 1024
377   QueueAppender app(&queue, 100);
378   size_t n = 1024 / sizeof(uint32_t);
379   for (uint32_t i = 0; i < n; ++i) {
380     app.writeBE(i);
381   }
382
383   // There must be a goodMallocSize between 100 and 1024...
384   EXPECT_LT(1, queue.front()->countChainElements());
385   const IOBuf* buf = queue.front();
386   do {
387     EXPECT_LE(100, buf->capacity());
388     buf = buf->next();
389   } while (buf != queue.front());
390
391   Cursor cursor(queue.front());
392   for (uint32_t i = 0; i < n; ++i) {
393     EXPECT_EQ(i, cursor.readBE<uint32_t>());
394   }
395
396   EXPECT_THROW({cursor.readBE<uint32_t>();}, std::out_of_range);
397 }
398
399 TEST(IOBuf, CursorOperators) {
400   // Test operators on a single-item chain
401   {
402     std::unique_ptr<IOBuf> chain1(IOBuf::create(20));
403     chain1->append(10);
404
405     Cursor curs1(chain1.get());
406     EXPECT_EQ(0, curs1 - chain1.get());
407     curs1.skip(3);
408     EXPECT_EQ(3, curs1 - chain1.get());
409     curs1.skip(7);
410     EXPECT_EQ(10, curs1 - chain1.get());
411
412     Cursor curs2(chain1.get());
413     EXPECT_EQ(0, curs2 - chain1.get());
414     EXPECT_EQ(10, curs1 - curs2);
415     EXPECT_THROW(curs2 - curs1, std::out_of_range);
416   }
417
418   // Test cross-chain operations
419   {
420     std::unique_ptr<IOBuf> chain1(IOBuf::create(20));
421     chain1->append(10);
422     std::unique_ptr<IOBuf> chain2 = chain1->clone();
423
424     Cursor curs1(chain1.get());
425     Cursor curs2(chain2.get());
426     EXPECT_THROW(curs1 - curs2, std::out_of_range);
427     EXPECT_THROW(curs1 - chain2.get(), std::out_of_range);
428   }
429
430   // Test operations on multi-item chains
431   {
432     std::unique_ptr<IOBuf> chain(IOBuf::create(20));
433     chain->append(10);
434     chain->appendChain(chain->clone());
435     EXPECT_EQ(20, chain->computeChainDataLength());
436
437     Cursor curs1(chain.get());
438     curs1.skip(5);
439     Cursor curs2(chain.get());
440     curs2.skip(3);
441     EXPECT_EQ(2, curs1 - curs2);
442     EXPECT_EQ(5, curs1 - chain.get());
443     EXPECT_THROW(curs2 - curs1, std::out_of_range);
444
445     curs1.skip(7);
446     EXPECT_EQ(9, curs1 - curs2);
447     EXPECT_EQ(12, curs1 - chain.get());
448     EXPECT_THROW(curs2 - curs1, std::out_of_range);
449
450     curs2.skip(7);
451     EXPECT_EQ(2, curs1 - curs2);
452     EXPECT_THROW(curs2 - curs1, std::out_of_range);
453   }
454 }
455
456 TEST(IOBuf, StringOperations) {
457   // Test a single buffer with two null-terminated strings and an extra uint8_t
458   // at the end
459   {
460     std::unique_ptr<IOBuf> chain(IOBuf::create(16));
461     Appender app(chain.get(), 0);
462     app.push(reinterpret_cast<const uint8_t*>("hello\0world\0\x01"), 13);
463
464     Cursor curs(chain.get());
465     EXPECT_STREQ("hello", curs.readTerminatedString().c_str());
466     EXPECT_STREQ("world", curs.readTerminatedString().c_str());
467     EXPECT_EQ(1, curs.read<uint8_t>());
468   }
469
470   // Test multiple buffers where the first is empty and the string starts in
471   // the second buffer.
472   {
473     std::unique_ptr<IOBuf> chain(IOBuf::create(8));
474     chain->prependChain(IOBuf::create(12));
475     Appender app(chain.get(), 0);
476     app.push(reinterpret_cast<const uint8_t*>("hello world\0"), 12);
477
478     Cursor curs(chain.get());
479     EXPECT_STREQ("hello world", curs.readTerminatedString().c_str());
480   }
481
482   // Test multiple buffers with a single null-terminated string spanning them
483   {
484     std::unique_ptr<IOBuf> chain(IOBuf::create(8));
485     chain->prependChain(IOBuf::create(8));
486     chain->append(8);
487     chain->next()->append(4);
488     RWPrivateCursor rwc(chain.get());
489     rwc.push(reinterpret_cast<const uint8_t*>("hello world\0"), 12);
490
491     Cursor curs(chain.get());
492     EXPECT_STREQ("hello world", curs.readTerminatedString().c_str());
493   }
494
495   // Test a reading a null-terminated string that's longer than the maximum
496   // allowable length
497   {
498     std::unique_ptr<IOBuf> chain(IOBuf::create(16));
499     Appender app(chain.get(), 0);
500     app.push(reinterpret_cast<const uint8_t*>("hello world\0"), 12);
501
502     Cursor curs(chain.get());
503     EXPECT_THROW(curs.readTerminatedString('\0', 5), std::length_error);
504   }
505
506   // Test reading a null-terminated string from a chain with an empty buffer at
507   // the front
508   {
509     std::unique_ptr<IOBuf> buf(IOBuf::create(8));
510     Appender app(buf.get(), 0);
511     app.push(reinterpret_cast<const uint8_t*>("hello\0"), 6);
512     std::unique_ptr<IOBuf> chain(IOBuf::create(8));
513     chain->prependChain(std::move(buf));
514
515     Cursor curs(chain.get());
516     EXPECT_STREQ("hello", curs.readTerminatedString().c_str());
517   }
518
519   // Test reading a two fixed-length strings from a single buffer with an extra
520   // uint8_t at the end
521   {
522     std::unique_ptr<IOBuf> chain(IOBuf::create(16));
523     Appender app(chain.get(), 0);
524     app.push(reinterpret_cast<const uint8_t*>("helloworld\x01"), 11);
525
526     Cursor curs(chain.get());
527     EXPECT_STREQ("hello", curs.readFixedString(5).c_str());
528     EXPECT_STREQ("world", curs.readFixedString(5).c_str());
529     EXPECT_EQ(1, curs.read<uint8_t>());
530   }
531
532   // Test multiple buffers where the first is empty and a fixed-length string
533   // starts in the second buffer.
534   {
535     std::unique_ptr<IOBuf> chain(IOBuf::create(8));
536     chain->prependChain(IOBuf::create(16));
537     Appender app(chain.get(), 0);
538     app.push(reinterpret_cast<const uint8_t*>("hello world"), 11);
539
540     Cursor curs(chain.get());
541     EXPECT_STREQ("hello world", curs.readFixedString(11).c_str());
542   }
543
544   // Test multiple buffers with a single fixed-length string spanning them
545   {
546     std::unique_ptr<IOBuf> chain(IOBuf::create(8));
547     chain->prependChain(IOBuf::create(8));
548     chain->append(7);
549     chain->next()->append(4);
550     RWPrivateCursor rwc(chain.get());
551     rwc.push(reinterpret_cast<const uint8_t*>("hello world"), 11);
552
553     Cursor curs(chain.get());
554     EXPECT_STREQ("hello world", curs.readFixedString(11).c_str());
555   }
556
557   // Test reading a fixed-length string from a chain with an empty buffer at
558   // the front
559   {
560     std::unique_ptr<IOBuf> buf(IOBuf::create(8));
561     Appender app(buf.get(), 0);
562     app.push(reinterpret_cast<const uint8_t*>("hello"), 5);
563     std::unique_ptr<IOBuf> chain(IOBuf::create(8));
564     chain->prependChain(std::move(buf));
565
566     Cursor curs(chain.get());
567     EXPECT_STREQ("hello", curs.readFixedString(5).c_str());
568   }
569 }
570
571 int benchmark_size = 1000;
572 unique_ptr<IOBuf> iobuf_benchmark;
573
574 unique_ptr<IOBuf> iobuf_read_benchmark;
575
576 template <class CursClass>
577 void runBenchmark() {
578   CursClass c(iobuf_benchmark.get());
579
580   for(int i = 0; i < benchmark_size; i++) {
581     c.write((uint8_t)0);
582   }
583 }
584
585 BENCHMARK(rwPrivateCursorBenchmark, iters) {
586   while (--iters) {
587     runBenchmark<RWPrivateCursor>();
588   }
589 }
590
591 BENCHMARK(rwUnshareCursorBenchmark, iters) {
592   while (--iters) {
593     runBenchmark<RWUnshareCursor>();
594   }
595 }
596
597
598 BENCHMARK(cursorBenchmark, iters) {
599   while (--iters) {
600     Cursor c(iobuf_read_benchmark.get());
601     for(int i = 0; i < benchmark_size ; i++) {
602       c.read<uint8_t>();
603     }
604   }
605 }
606
607 BENCHMARK(skipBenchmark, iters) {
608   uint8_t buf;
609   while (--iters) {
610     Cursor c(iobuf_read_benchmark.get());
611     for(int i = 0; i < benchmark_size ; i++) {
612       c.peek();
613       c.skip(1);
614     }
615   }
616 }
617
618 // fbmake opt
619 // _bin/folly/experimental/io/test/iobuf_cursor_test -benchmark
620 //
621 // Benchmark                               Iters   Total t    t/iter iter/sec
622 // ---------------------------------------------------------------------------
623 // rwPrivateCursorBenchmark               100000  142.9 ms  1.429 us  683.5 k
624 // rwUnshareCursorBenchmark               100000  309.3 ms  3.093 us  315.7 k
625 // cursorBenchmark                        100000  741.4 ms  7.414 us  131.7 k
626 // skipBenchmark                          100000  738.9 ms  7.389 us  132.2 k
627 //
628 // uname -a:
629 //
630 // Linux dev2159.snc6.facebook.com 2.6.33-7_fbk15_104e4d0 #1 SMP
631 // Tue Oct 19 22:40:30 PDT 2010 x86_64 x86_64 x86_64 GNU/Linux
632 //
633 // 72GB RAM, 2 CPUs (Intel(R) Xeon(R) CPU L5630  @ 2.13GHz)
634 // hyperthreading disabled
635
636 int main(int argc, char** argv) {
637   testing::InitGoogleTest(&argc, argv);
638   google::ParseCommandLineFlags(&argc, &argv, true);
639
640   auto ret = RUN_ALL_TESTS();
641
642   if (ret == 0 && FLAGS_benchmark) {
643     iobuf_benchmark = IOBuf::create(benchmark_size);
644     iobuf_benchmark->append(benchmark_size);
645
646     iobuf_read_benchmark = IOBuf::create(1);
647     for (int i = 0; i < benchmark_size; i++) {
648       unique_ptr<IOBuf> iobuf2(IOBuf::create(1));
649       iobuf2->append(1);
650       iobuf_read_benchmark->prependChain(std::move(iobuf2));
651     }
652
653     folly::runBenchmarks();
654   }
655
656   return ret;
657 }