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