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