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