58ad41d7e6eaec01b2c05ded1a9ff2587b9fd4f9
[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, pushCursorData) {
248   unique_ptr<IOBuf> iobuf1(IOBuf::create(20));
249   iobuf1->append(15);
250   iobuf1->trimStart(5);
251   unique_ptr<IOBuf> iobuf2(IOBuf::create(10));
252   unique_ptr<IOBuf> iobuf3(IOBuf::create(10));
253   iobuf3->append(10);
254
255   iobuf1->prependChain(std::move(iobuf2));
256   iobuf1->prependChain(std::move(iobuf3));
257   EXPECT_TRUE(iobuf1->isChained());
258
259   //write 20 bytes to the buffer chain
260   RWPrivateCursor wcursor(iobuf1.get());
261   wcursor.writeBE<uint64_t>(1);
262   wcursor.writeBE<uint64_t>(10);
263   wcursor.writeBE<uint32_t>(20);
264
265   // create a read buffer for the buffer chain
266   Cursor rcursor(iobuf1.get());
267   EXPECT_EQ(1, rcursor.readBE<uint64_t>());
268   EXPECT_EQ(10, rcursor.readBE<uint64_t>());
269   EXPECT_EQ(20, rcursor.readBE<uint32_t>());
270   EXPECT_EQ(0, rcursor.totalLength());
271   rcursor.reset(iobuf1.get());
272   EXPECT_EQ(20, rcursor.totalLength());
273
274   // create another write buffer
275   unique_ptr<IOBuf> iobuf4(IOBuf::create(30));
276   iobuf4->append(30);
277   RWPrivateCursor wcursor2(iobuf4.get());
278   // write buffer chain data into it, now wcursor2 should only
279   // have 10 bytes writable space
280   wcursor2.push(rcursor, 20);
281   EXPECT_EQ(wcursor2.totalLength(), 10);
282   // write again with not enough space in rcursor
283   EXPECT_THROW(wcursor2.push(rcursor, 20), std::out_of_range);
284
285   // create a read cursor to check iobuf3 data back
286   Cursor rcursor2(iobuf4.get());
287   EXPECT_EQ(1, rcursor2.readBE<uint64_t>());
288   EXPECT_EQ(10, rcursor2.readBE<uint64_t>());
289   EXPECT_EQ(20, rcursor2.readBE<uint32_t>());
290
291 }
292
293 TEST(IOBuf, Gather) {
294   std::unique_ptr<IOBuf> iobuf1(IOBuf::create(10));
295   append(iobuf1, "he");
296   std::unique_ptr<IOBuf> iobuf2(IOBuf::create(10));
297   append(iobuf2, "llo ");
298   std::unique_ptr<IOBuf> iobuf3(IOBuf::create(10));
299   append(iobuf3, "world");
300   iobuf1->prependChain(std::move(iobuf2));
301   iobuf1->prependChain(std::move(iobuf3));
302   EXPECT_EQ(3, iobuf1->countChainElements());
303   EXPECT_EQ(11, iobuf1->computeChainDataLength());
304
305   // Attempting to gather() more data than available in the chain should fail.
306   // Try from the very beginning of the chain.
307   RWPrivateCursor cursor(iobuf1.get());
308   EXPECT_THROW(cursor.gather(15), std::overflow_error);
309   // Now try from the middle of the chain
310   cursor += 3;
311   EXPECT_THROW(cursor.gather(10), std::overflow_error);
312
313   // Calling gatherAtMost() should succeed, however, and just gather
314   // as much as it can
315   cursor.gatherAtMost(10);
316   EXPECT_EQ(8, cursor.length());
317   EXPECT_EQ(8, cursor.totalLength());
318   EXPECT_EQ("lo world",
319             folly::StringPiece(reinterpret_cast<const char*>(cursor.data()),
320                                cursor.length()));
321   EXPECT_EQ(2, iobuf1->countChainElements());
322   EXPECT_EQ(11, iobuf1->computeChainDataLength());
323
324   // Now try gather again on the chain head
325   cursor = RWPrivateCursor(iobuf1.get());
326   cursor.gather(5);
327   // Since gather() doesn't split buffers, everything should be collapsed into
328   // a single buffer now.
329   EXPECT_EQ(1, iobuf1->countChainElements());
330   EXPECT_EQ(11, iobuf1->computeChainDataLength());
331   EXPECT_EQ(11, cursor.length());
332   EXPECT_EQ(11, cursor.totalLength());
333 }
334
335 TEST(IOBuf, cloneAndInsert) {
336   std::unique_ptr<IOBuf> iobuf1(IOBuf::create(10));
337   append(iobuf1, "he");
338   std::unique_ptr<IOBuf> iobuf2(IOBuf::create(10));
339   append(iobuf2, "llo ");
340   std::unique_ptr<IOBuf> iobuf3(IOBuf::create(10));
341   append(iobuf3, "world");
342   iobuf1->prependChain(std::move(iobuf2));
343   iobuf1->prependChain(std::move(iobuf3));
344   EXPECT_EQ(3, iobuf1->countChainElements());
345   EXPECT_EQ(11, iobuf1->computeChainDataLength());
346
347   std::unique_ptr<IOBuf> cloned;
348
349   Cursor(iobuf1.get()).clone(cloned, 3);
350   EXPECT_EQ(2, cloned->countChainElements());
351   EXPECT_EQ(3, cloned->computeChainDataLength());
352
353
354   EXPECT_EQ(11, Cursor(iobuf1.get()).cloneAtMost(cloned, 20));
355   EXPECT_EQ(3, cloned->countChainElements());
356   EXPECT_EQ(11, cloned->computeChainDataLength());
357
358
359   EXPECT_THROW({Cursor(iobuf1.get()).clone(cloned, 20);},
360                std::out_of_range);
361
362   {
363     // Check that inserting in the middle of an iobuf splits
364     RWPrivateCursor cursor(iobuf1.get());
365     Cursor(iobuf1.get()).clone(cloned, 3);
366     EXPECT_EQ(2, cloned->countChainElements());
367     EXPECT_EQ(3, cloned->computeChainDataLength());
368
369     cursor.skip(1);
370
371     cursor.insert(std::move(cloned));
372     cursor.insert(folly::IOBuf::create(0));
373     EXPECT_EQ(7, iobuf1->countChainElements());
374     EXPECT_EQ(14, iobuf1->computeChainDataLength());
375     // Check that nextBuf got set correctly to the buffer with 1 byte left
376     EXPECT_EQ(1, cursor.peek().second);
377     cursor.read<uint8_t>();
378   }
379
380   {
381     // Check that inserting at the end doesn't create empty buf
382     RWPrivateCursor cursor(iobuf1.get());
383     Cursor(iobuf1.get()).clone(cloned, 1);
384     EXPECT_EQ(1, cloned->countChainElements());
385     EXPECT_EQ(1, cloned->computeChainDataLength());
386
387     cursor.skip(1);
388
389     cursor.insert(std::move(cloned));
390     EXPECT_EQ(8, iobuf1->countChainElements());
391     EXPECT_EQ(15, iobuf1->computeChainDataLength());
392     // Check that nextBuf got set correctly
393     cursor.read<uint8_t>();
394   }
395   {
396     // Check that inserting at the beginning doesn't create empty buf
397     RWPrivateCursor cursor(iobuf1.get());
398     Cursor(iobuf1.get()).clone(cloned, 1);
399     EXPECT_EQ(1, cloned->countChainElements());
400     EXPECT_EQ(1, cloned->computeChainDataLength());
401
402     cursor.insert(std::move(cloned));
403     EXPECT_EQ(9, iobuf1->countChainElements());
404     EXPECT_EQ(16, iobuf1->computeChainDataLength());
405     // Check that nextBuf got set correctly
406     cursor.read<uint8_t>();
407   }
408 }
409
410 TEST(IOBuf, Appender) {
411   std::unique_ptr<IOBuf> head(IOBuf::create(10));
412   append(head, "hello");
413
414   Appender app(head.get(), 10);
415   uint32_t cap = head->capacity();
416   uint32_t len1 = app.length();
417   EXPECT_EQ(cap - 5, len1);
418   app.ensure(len1);  // won't grow
419   EXPECT_EQ(len1, app.length());
420   app.ensure(len1 + 1);  // will grow
421   EXPECT_LE(len1 + 1, app.length());
422
423   append(app, " world");
424   EXPECT_EQ("hello world", toString(*head));
425 }
426
427 TEST(IOBuf, QueueAppender) {
428   folly::IOBufQueue queue;
429
430   // Allocate 100 bytes at once, but don't grow past 1024
431   QueueAppender app(&queue, 100);
432   size_t n = 1024 / sizeof(uint32_t);
433   for (uint32_t i = 0; i < n; ++i) {
434     app.writeBE(i);
435   }
436
437   // There must be a goodMallocSize between 100 and 1024...
438   EXPECT_LT(1, queue.front()->countChainElements());
439   const IOBuf* buf = queue.front();
440   do {
441     EXPECT_LE(100, buf->capacity());
442     buf = buf->next();
443   } while (buf != queue.front());
444
445   Cursor cursor(queue.front());
446   for (uint32_t i = 0; i < n; ++i) {
447     EXPECT_EQ(i, cursor.readBE<uint32_t>());
448   }
449
450   EXPECT_THROW({cursor.readBE<uint32_t>();}, std::out_of_range);
451 }
452
453 TEST(IOBuf, CursorOperators) {
454   // Test operators on a single-item chain
455   {
456     std::unique_ptr<IOBuf> chain1(IOBuf::create(20));
457     chain1->append(10);
458
459     Cursor curs1(chain1.get());
460     EXPECT_EQ(0, curs1 - chain1.get());
461     curs1.skip(3);
462     EXPECT_EQ(3, curs1 - chain1.get());
463     curs1.skip(7);
464     EXPECT_EQ(10, curs1 - chain1.get());
465
466     Cursor curs2(chain1.get());
467     EXPECT_EQ(0, curs2 - chain1.get());
468     EXPECT_EQ(10, curs1 - curs2);
469     EXPECT_THROW(curs2 - curs1, std::out_of_range);
470   }
471
472   // Test cross-chain operations
473   {
474     std::unique_ptr<IOBuf> chain1(IOBuf::create(20));
475     chain1->append(10);
476     std::unique_ptr<IOBuf> chain2 = chain1->clone();
477
478     Cursor curs1(chain1.get());
479     Cursor curs2(chain2.get());
480     EXPECT_THROW(curs1 - curs2, std::out_of_range);
481     EXPECT_THROW(curs1 - chain2.get(), std::out_of_range);
482   }
483
484   // Test operations on multi-item chains
485   {
486     std::unique_ptr<IOBuf> chain(IOBuf::create(20));
487     chain->append(10);
488     chain->appendChain(chain->clone());
489     EXPECT_EQ(20, chain->computeChainDataLength());
490
491     Cursor curs1(chain.get());
492     curs1.skip(5);
493     Cursor curs2(chain.get());
494     curs2.skip(3);
495     EXPECT_EQ(2, curs1 - curs2);
496     EXPECT_EQ(5, curs1 - chain.get());
497     EXPECT_THROW(curs2 - curs1, std::out_of_range);
498
499     curs1.skip(7);
500     EXPECT_EQ(9, curs1 - curs2);
501     EXPECT_EQ(12, curs1 - chain.get());
502     EXPECT_THROW(curs2 - curs1, std::out_of_range);
503
504     curs2.skip(7);
505     EXPECT_EQ(2, curs1 - curs2);
506     EXPECT_THROW(curs2 - curs1, std::out_of_range);
507   }
508 }
509
510 TEST(IOBuf, StringOperations) {
511   // Test a single buffer with two null-terminated strings and an extra uint8_t
512   // at the end
513   {
514     std::unique_ptr<IOBuf> chain(IOBuf::create(16));
515     Appender app(chain.get(), 0);
516     app.push(reinterpret_cast<const uint8_t*>("hello\0world\0\x01"), 13);
517
518     Cursor curs(chain.get());
519     EXPECT_STREQ("hello", curs.readTerminatedString().c_str());
520     EXPECT_STREQ("world", curs.readTerminatedString().c_str());
521     EXPECT_EQ(1, curs.read<uint8_t>());
522   }
523
524   // Test multiple buffers where the first is empty and the string starts in
525   // the second buffer.
526   {
527     std::unique_ptr<IOBuf> chain(IOBuf::create(8));
528     chain->prependChain(IOBuf::create(12));
529     Appender app(chain.get(), 0);
530     app.push(reinterpret_cast<const uint8_t*>("hello world\0"), 12);
531
532     Cursor curs(chain.get());
533     EXPECT_STREQ("hello world", curs.readTerminatedString().c_str());
534   }
535
536   // Test multiple buffers with a single null-terminated string spanning them
537   {
538     std::unique_ptr<IOBuf> chain(IOBuf::create(8));
539     chain->prependChain(IOBuf::create(8));
540     chain->append(8);
541     chain->next()->append(4);
542     RWPrivateCursor rwc(chain.get());
543     rwc.push(reinterpret_cast<const uint8_t*>("hello world\0"), 12);
544
545     Cursor curs(chain.get());
546     EXPECT_STREQ("hello world", curs.readTerminatedString().c_str());
547   }
548
549   // Test a reading a null-terminated string that's longer than the maximum
550   // allowable length
551   {
552     std::unique_ptr<IOBuf> chain(IOBuf::create(16));
553     Appender app(chain.get(), 0);
554     app.push(reinterpret_cast<const uint8_t*>("hello world\0"), 12);
555
556     Cursor curs(chain.get());
557     EXPECT_THROW(curs.readTerminatedString('\0', 5), std::length_error);
558   }
559
560   // Test reading a null-terminated string from a chain with an empty buffer at
561   // the front
562   {
563     std::unique_ptr<IOBuf> buf(IOBuf::create(8));
564     Appender app(buf.get(), 0);
565     app.push(reinterpret_cast<const uint8_t*>("hello\0"), 6);
566     std::unique_ptr<IOBuf> chain(IOBuf::create(8));
567     chain->prependChain(std::move(buf));
568
569     Cursor curs(chain.get());
570     EXPECT_STREQ("hello", curs.readTerminatedString().c_str());
571   }
572
573   // Test reading a two fixed-length strings from a single buffer with an extra
574   // uint8_t at the end
575   {
576     std::unique_ptr<IOBuf> chain(IOBuf::create(16));
577     Appender app(chain.get(), 0);
578     app.push(reinterpret_cast<const uint8_t*>("helloworld\x01"), 11);
579
580     Cursor curs(chain.get());
581     EXPECT_STREQ("hello", curs.readFixedString(5).c_str());
582     EXPECT_STREQ("world", curs.readFixedString(5).c_str());
583     EXPECT_EQ(1, curs.read<uint8_t>());
584   }
585
586   // Test multiple buffers where the first is empty and a fixed-length string
587   // starts in the second buffer.
588   {
589     std::unique_ptr<IOBuf> chain(IOBuf::create(8));
590     chain->prependChain(IOBuf::create(16));
591     Appender app(chain.get(), 0);
592     app.push(reinterpret_cast<const uint8_t*>("hello world"), 11);
593
594     Cursor curs(chain.get());
595     EXPECT_STREQ("hello world", curs.readFixedString(11).c_str());
596   }
597
598   // Test multiple buffers with a single fixed-length string spanning them
599   {
600     std::unique_ptr<IOBuf> chain(IOBuf::create(8));
601     chain->prependChain(IOBuf::create(8));
602     chain->append(7);
603     chain->next()->append(4);
604     RWPrivateCursor rwc(chain.get());
605     rwc.push(reinterpret_cast<const uint8_t*>("hello world"), 11);
606
607     Cursor curs(chain.get());
608     EXPECT_STREQ("hello world", curs.readFixedString(11).c_str());
609   }
610
611   // Test reading a fixed-length string from a chain with an empty buffer at
612   // the front
613   {
614     std::unique_ptr<IOBuf> buf(IOBuf::create(8));
615     Appender app(buf.get(), 0);
616     app.push(reinterpret_cast<const uint8_t*>("hello"), 5);
617     std::unique_ptr<IOBuf> chain(IOBuf::create(8));
618     chain->prependChain(std::move(buf));
619
620     Cursor curs(chain.get());
621     EXPECT_STREQ("hello", curs.readFixedString(5).c_str());
622   }
623 }
624
625 int benchmark_size = 1000;
626 unique_ptr<IOBuf> iobuf_benchmark;
627
628 unique_ptr<IOBuf> iobuf_read_benchmark;
629
630 template <class CursClass>
631 void runBenchmark() {
632   CursClass c(iobuf_benchmark.get());
633
634   for(int i = 0; i < benchmark_size; i++) {
635     c.write((uint8_t)0);
636   }
637 }
638
639 BENCHMARK(rwPrivateCursorBenchmark, iters) {
640   while (iters--) {
641     runBenchmark<RWPrivateCursor>();
642   }
643 }
644
645 BENCHMARK(rwUnshareCursorBenchmark, iters) {
646   while (iters--) {
647     runBenchmark<RWUnshareCursor>();
648   }
649 }
650
651
652 BENCHMARK(cursorBenchmark, iters) {
653   while (iters--) {
654     Cursor c(iobuf_read_benchmark.get());
655     for(int i = 0; i < benchmark_size ; i++) {
656       c.read<uint8_t>();
657     }
658   }
659 }
660
661 BENCHMARK(skipBenchmark, iters) {
662   uint8_t buf;
663   while (iters--) {
664     Cursor c(iobuf_read_benchmark.get());
665     for(int i = 0; i < benchmark_size ; i++) {
666       c.peek();
667       c.skip(1);
668     }
669   }
670 }
671
672 // fbmake opt
673 // _bin/folly/experimental/io/test/iobuf_cursor_test -benchmark
674 //
675 // Benchmark                               Iters   Total t    t/iter iter/sec
676 // ---------------------------------------------------------------------------
677 // rwPrivateCursorBenchmark               100000  142.9 ms  1.429 us  683.5 k
678 // rwUnshareCursorBenchmark               100000  309.3 ms  3.093 us  315.7 k
679 // cursorBenchmark                        100000  741.4 ms  7.414 us  131.7 k
680 // skipBenchmark                          100000  738.9 ms  7.389 us  132.2 k
681 //
682 // uname -a:
683 //
684 // Linux dev2159.snc6.facebook.com 2.6.33-7_fbk15_104e4d0 #1 SMP
685 // Tue Oct 19 22:40:30 PDT 2010 x86_64 x86_64 x86_64 GNU/Linux
686 //
687 // 72GB RAM, 2 CPUs (Intel(R) Xeon(R) CPU L5630  @ 2.13GHz)
688 // hyperthreading disabled
689
690 int main(int argc, char** argv) {
691   testing::InitGoogleTest(&argc, argv);
692   gflags::ParseCommandLineFlags(&argc, &argv, true);
693
694   auto ret = RUN_ALL_TESTS();
695
696   if (ret == 0 && FLAGS_benchmark) {
697     iobuf_benchmark = IOBuf::create(benchmark_size);
698     iobuf_benchmark->append(benchmark_size);
699
700     iobuf_read_benchmark = IOBuf::create(1);
701     for (int i = 0; i < benchmark_size; i++) {
702       unique_ptr<IOBuf> iobuf2(IOBuf::create(1));
703       iobuf2->append(1);
704       iobuf_read_benchmark->prependChain(std::move(iobuf2));
705     }
706
707     folly::runBenchmarks();
708   }
709
710   return ret;
711 }