958f6d4589fea1cddf8ce1fa87dcb5537aed91f7
[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   ByteRange b;
186   while (!(b = cursor.peekBytes()).empty()) {
187     str.append(reinterpret_cast<const char*>(b.data()), b.size());
188     cursor.skip(b.size());
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 b = cursor.peekBytes();
222     EXPECT_EQ("he", StringPiece(b));
223     cursor.skip(b.size());
224     b = cursor.peekBytes();
225     EXPECT_EQ("llo ", StringPiece(b));
226     cursor.skip(b.size());
227     b = cursor.peekBytes();
228     EXPECT_EQ("world", StringPiece(b));
229     cursor.skip(b.size());
230     EXPECT_EQ(3, iobuf1->countChainElements());
231     EXPECT_EQ(11, iobuf1->computeChainDataLength());
232   }
233
234   {
235     RWPrivateCursor cursor(iobuf1.get());
236     cursor.gather(11);
237     auto b = cursor.peekBytes();
238     EXPECT_EQ("hello world", StringPiece(b));
239     EXPECT_EQ(1, iobuf1->countChainElements());
240     EXPECT_EQ(11, iobuf1->computeChainDataLength());
241   }
242 }
243
244 TEST(IOBuf, pushCursorData) {
245   unique_ptr<IOBuf> iobuf1(IOBuf::create(20));
246   iobuf1->append(15);
247   iobuf1->trimStart(5);
248   unique_ptr<IOBuf> iobuf2(IOBuf::create(10));
249   unique_ptr<IOBuf> iobuf3(IOBuf::create(10));
250   iobuf3->append(10);
251
252   iobuf1->prependChain(std::move(iobuf2));
253   iobuf1->prependChain(std::move(iobuf3));
254   EXPECT_TRUE(iobuf1->isChained());
255
256   //write 20 bytes to the buffer chain
257   RWPrivateCursor wcursor(iobuf1.get());
258   EXPECT_FALSE(wcursor.isAtEnd());
259   wcursor.writeBE<uint64_t>(1);
260   wcursor.writeBE<uint64_t>(10);
261   wcursor.writeBE<uint32_t>(20);
262   EXPECT_TRUE(wcursor.isAtEnd());
263
264   // create a read buffer for the buffer chain
265   Cursor rcursor(iobuf1.get());
266   EXPECT_EQ(1, rcursor.readBE<uint64_t>());
267   EXPECT_EQ(10, rcursor.readBE<uint64_t>());
268   EXPECT_EQ(20, rcursor.readBE<uint32_t>());
269   EXPECT_EQ(0, rcursor.totalLength());
270   rcursor.reset(iobuf1.get());
271   EXPECT_EQ(20, rcursor.totalLength());
272
273   // create another write buffer
274   unique_ptr<IOBuf> iobuf4(IOBuf::create(30));
275   iobuf4->append(30);
276   RWPrivateCursor wcursor2(iobuf4.get());
277   // write buffer chain data into it, now wcursor2 should only
278   // have 10 bytes writable space
279   wcursor2.push(rcursor, 20);
280   EXPECT_EQ(wcursor2.totalLength(), 10);
281   // write again with not enough space in rcursor
282   EXPECT_THROW(wcursor2.push(rcursor, 20), std::out_of_range);
283
284   // create a read cursor to check iobuf3 data back
285   Cursor rcursor2(iobuf4.get());
286   EXPECT_EQ(1, rcursor2.readBE<uint64_t>());
287   EXPECT_EQ(10, rcursor2.readBE<uint64_t>());
288   EXPECT_EQ(20, rcursor2.readBE<uint32_t>());
289
290 }
291
292 TEST(IOBuf, Gather) {
293   std::unique_ptr<IOBuf> iobuf1(IOBuf::create(10));
294   append(iobuf1, "he");
295   std::unique_ptr<IOBuf> iobuf2(IOBuf::create(10));
296   append(iobuf2, "llo ");
297   std::unique_ptr<IOBuf> iobuf3(IOBuf::create(10));
298   append(iobuf3, "world");
299   iobuf1->prependChain(std::move(iobuf2));
300   iobuf1->prependChain(std::move(iobuf3));
301   EXPECT_EQ(3, iobuf1->countChainElements());
302   EXPECT_EQ(11, iobuf1->computeChainDataLength());
303
304   // Attempting to gather() more data than available in the chain should fail.
305   // Try from the very beginning of the chain.
306   RWPrivateCursor cursor(iobuf1.get());
307   EXPECT_THROW(cursor.gather(15), std::overflow_error);
308   // Now try from the middle of the chain
309   cursor += 3;
310   EXPECT_THROW(cursor.gather(10), std::overflow_error);
311
312   // Calling gatherAtMost() should succeed, however, and just gather
313   // as much as it can
314   cursor.gatherAtMost(10);
315   EXPECT_EQ(8, cursor.length());
316   EXPECT_EQ(8, cursor.totalLength());
317   EXPECT_FALSE(cursor.isAtEnd());
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.peekBytes().size());
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, cloneWithEmptyBufAtStart) {
411   folly::IOBufEqual eq;
412   auto empty = IOBuf::create(0);
413   auto hel = IOBuf::create(3);
414   append(hel, "hel");
415   auto lo = IOBuf::create(2);
416   append(lo, "lo");
417
418   auto iobuf = empty->clone();
419   iobuf->prependChain(hel->clone());
420   iobuf->prependChain(lo->clone());
421   iobuf->prependChain(empty->clone());
422   iobuf->prependChain(hel->clone());
423   iobuf->prependChain(lo->clone());
424   iobuf->prependChain(empty->clone());
425   iobuf->prependChain(lo->clone());
426   iobuf->prependChain(hel->clone());
427   iobuf->prependChain(lo->clone());
428   iobuf->prependChain(lo->clone());
429
430   Cursor cursor(iobuf.get());
431   std::unique_ptr<IOBuf> cloned;
432   char data[3];
433   cursor.pull(&data, 3);
434   cursor.clone(cloned, 2);
435   EXPECT_EQ(1, cloned->countChainElements());
436   EXPECT_EQ(2, cloned->length());
437   EXPECT_TRUE(eq(lo, cloned));
438
439   cursor.pull(&data, 3);
440   EXPECT_EQ("hel", std::string(data, sizeof(data)));
441
442   cursor.skip(2);
443   cursor.clone(cloned, 2);
444   EXPECT_TRUE(eq(lo, cloned));
445
446   std::string hello = cursor.readFixedString(5);
447   cursor.clone(cloned, 2);
448   EXPECT_TRUE(eq(lo, cloned));
449 }
450
451 TEST(IOBuf, Appender) {
452   std::unique_ptr<IOBuf> head(IOBuf::create(10));
453   append(head, "hello");
454
455   Appender app(head.get(), 10);
456   uint32_t cap = head->capacity();
457   uint32_t len1 = app.length();
458   EXPECT_EQ(cap - 5, len1);
459   app.ensure(len1);  // won't grow
460   EXPECT_EQ(len1, app.length());
461   app.ensure(len1 + 1);  // will grow
462   EXPECT_LE(len1 + 1, app.length());
463
464   append(app, " world");
465   EXPECT_EQ("hello world", toString(*head));
466 }
467
468 TEST(IOBuf, Printf) {
469   IOBuf head(IOBuf::CREATE, 24);
470   Appender app(&head, 32);
471
472   app.printf("%s", "test");
473   EXPECT_EQ(head.length(), 4);
474   EXPECT_EQ(0, memcmp(head.data(), "test\0", 5));
475
476   app.printf("%d%s %s%s %#x", 32, "this string is",
477              "longer than our original allocation size,",
478              "and will therefore require a new allocation", 0x12345678);
479   // The tailroom should start with a nul byte now.
480   EXPECT_GE(head.prev()->tailroom(), 1);
481   EXPECT_EQ(0, *head.prev()->tail());
482
483   EXPECT_EQ("test32this string is longer than our original "
484             "allocation size,and will therefore require a "
485             "new allocation 0x12345678",
486             head.moveToFbString().toStdString());
487 }
488
489 TEST(IOBuf, Format) {
490   IOBuf head(IOBuf::CREATE, 24);
491   Appender app(&head, 32);
492
493   format("{}", "test")(app);
494   EXPECT_EQ(head.length(), 4);
495   EXPECT_EQ(0, memcmp(head.data(), "test", 4));
496
497   auto fmt = format("{}{} {}{} {:#x}",
498                     32, "this string is",
499                     "longer than our original allocation size,",
500                     "and will therefore require a new allocation",
501                     0x12345678);
502   fmt(app);
503   EXPECT_EQ("test32this string is longer than our original "
504             "allocation size,and will therefore require a "
505             "new allocation 0x12345678",
506             head.moveToFbString().toStdString());
507 }
508
509 TEST(IOBuf, QueueAppender) {
510   folly::IOBufQueue queue;
511
512   // Allocate 100 bytes at once, but don't grow past 1024
513   QueueAppender app(&queue, 100);
514   size_t n = 1024 / sizeof(uint32_t);
515   for (uint32_t i = 0; i < n; ++i) {
516     app.writeBE(i);
517   }
518
519   // There must be a goodMallocSize between 100 and 1024...
520   EXPECT_LT(1, queue.front()->countChainElements());
521   const IOBuf* buf = queue.front();
522   do {
523     EXPECT_LE(100, buf->capacity());
524     buf = buf->next();
525   } while (buf != queue.front());
526
527   Cursor cursor(queue.front());
528   for (uint32_t i = 0; i < n; ++i) {
529     EXPECT_EQ(i, cursor.readBE<uint32_t>());
530   }
531
532   EXPECT_THROW({cursor.readBE<uint32_t>();}, std::out_of_range);
533 }
534
535 TEST(IOBuf, CursorOperators) {
536   // Test operators on a single-item chain
537   {
538     std::unique_ptr<IOBuf> chain1(IOBuf::create(20));
539     chain1->append(10);
540
541     Cursor curs1(chain1.get());
542     EXPECT_EQ(0, curs1 - chain1.get());
543     EXPECT_FALSE(curs1.isAtEnd());
544     curs1.skip(3);
545     EXPECT_EQ(3, curs1 - chain1.get());
546     EXPECT_FALSE(curs1.isAtEnd());
547     curs1.skip(7);
548     EXPECT_EQ(10, curs1 - chain1.get());
549     EXPECT_TRUE(curs1.isAtEnd());
550
551     Cursor curs2(chain1.get());
552     EXPECT_EQ(0, curs2 - chain1.get());
553     EXPECT_EQ(10, curs1 - curs2);
554     EXPECT_THROW(curs2 - curs1, std::out_of_range);
555   }
556
557   // Test cross-chain operations
558   {
559     std::unique_ptr<IOBuf> chain1(IOBuf::create(20));
560     chain1->append(10);
561     std::unique_ptr<IOBuf> chain2 = chain1->clone();
562
563     Cursor curs1(chain1.get());
564     Cursor curs2(chain2.get());
565     EXPECT_THROW(curs1 - curs2, std::out_of_range);
566     EXPECT_THROW(curs1 - chain2.get(), std::out_of_range);
567   }
568
569   // Test operations on multi-item chains
570   {
571     std::unique_ptr<IOBuf> chain(IOBuf::create(20));
572     chain->append(10);
573     chain->appendChain(chain->clone());
574     EXPECT_EQ(20, chain->computeChainDataLength());
575
576     Cursor curs1(chain.get());
577     curs1.skip(5);
578     Cursor curs2(chain.get());
579     curs2.skip(3);
580     EXPECT_EQ(2, curs1 - curs2);
581     EXPECT_EQ(5, curs1 - chain.get());
582     EXPECT_THROW(curs2 - curs1, std::out_of_range);
583
584     curs1.skip(7);
585     EXPECT_EQ(9, curs1 - curs2);
586     EXPECT_EQ(12, curs1 - chain.get());
587     EXPECT_THROW(curs2 - curs1, std::out_of_range);
588
589     curs2.skip(7);
590     EXPECT_EQ(2, curs1 - curs2);
591     EXPECT_THROW(curs2 - curs1, std::out_of_range);
592   }
593
594   // Test isAtEnd() with empty buffers at the end of a chain
595   {
596     auto iobuf1 = IOBuf::create(20);
597     iobuf1->append(15);
598     iobuf1->trimStart(5);
599
600     Cursor c(iobuf1.get());
601     EXPECT_FALSE(c.isAtEnd());
602     c.skip(10);
603     EXPECT_TRUE(c.isAtEnd());
604
605     iobuf1->prependChain(IOBuf::create(10));
606     iobuf1->prependChain(IOBuf::create(10));
607     EXPECT_TRUE(c.isAtEnd());
608     iobuf1->prev()->append(5);
609     EXPECT_FALSE(c.isAtEnd());
610     c.skip(5);
611     EXPECT_TRUE(c.isAtEnd());
612   }
613
614   // Test canAdvance with a chain of items
615   {
616     auto chain = IOBuf::create(10);
617     chain->append(10);
618     chain->appendChain(chain->clone());
619     EXPECT_EQ(2, chain->countChainElements());
620     EXPECT_EQ(20, chain->computeChainDataLength());
621
622     Cursor c(chain.get());
623     for (size_t i = 0; i <= 20; ++i) {
624       EXPECT_TRUE(c.canAdvance(i));
625     }
626     EXPECT_FALSE(c.canAdvance(21));
627     c.skip(10);
628     EXPECT_TRUE(c.canAdvance(10));
629     EXPECT_FALSE(c.canAdvance(11));
630   }
631 }
632
633 TEST(IOBuf, StringOperations) {
634   // Test a single buffer with two null-terminated strings and an extra uint8_t
635   // at the end
636   {
637     std::unique_ptr<IOBuf> chain(IOBuf::create(16));
638     Appender app(chain.get(), 0);
639     app.push(reinterpret_cast<const uint8_t*>("hello\0world\0\x01"), 13);
640
641     Cursor curs(chain.get());
642     EXPECT_STREQ("hello", curs.readTerminatedString().c_str());
643     EXPECT_STREQ("world", curs.readTerminatedString().c_str());
644     EXPECT_EQ(1, curs.read<uint8_t>());
645   }
646
647   // Test multiple buffers where the first is empty and the string starts in
648   // the second buffer.
649   {
650     std::unique_ptr<IOBuf> chain(IOBuf::create(8));
651     chain->prependChain(IOBuf::create(12));
652     Appender app(chain.get(), 0);
653     app.push(reinterpret_cast<const uint8_t*>("hello world\0"), 12);
654
655     Cursor curs(chain.get());
656     EXPECT_STREQ("hello world", curs.readTerminatedString().c_str());
657   }
658
659   // Test multiple buffers with a single null-terminated string spanning them
660   {
661     std::unique_ptr<IOBuf> chain(IOBuf::create(8));
662     chain->prependChain(IOBuf::create(8));
663     chain->append(8);
664     chain->next()->append(4);
665     RWPrivateCursor rwc(chain.get());
666     rwc.push(reinterpret_cast<const uint8_t*>("hello world\0"), 12);
667
668     Cursor curs(chain.get());
669     EXPECT_STREQ("hello world", curs.readTerminatedString().c_str());
670   }
671
672   // Test a reading a null-terminated string that's longer than the maximum
673   // allowable length
674   {
675     std::unique_ptr<IOBuf> chain(IOBuf::create(16));
676     Appender app(chain.get(), 0);
677     app.push(reinterpret_cast<const uint8_t*>("hello world\0"), 12);
678
679     Cursor curs(chain.get());
680     EXPECT_THROW(curs.readTerminatedString('\0', 5), std::length_error);
681   }
682
683   // Test reading a null-terminated string from a chain with an empty buffer at
684   // the front
685   {
686     std::unique_ptr<IOBuf> buf(IOBuf::create(8));
687     Appender app(buf.get(), 0);
688     app.push(reinterpret_cast<const uint8_t*>("hello\0"), 6);
689     std::unique_ptr<IOBuf> chain(IOBuf::create(8));
690     chain->prependChain(std::move(buf));
691
692     Cursor curs(chain.get());
693     EXPECT_STREQ("hello", curs.readTerminatedString().c_str());
694   }
695
696   // Test reading a null-terminated string from a chain that doesn't contain the
697   // terminator
698   {
699     std::unique_ptr<IOBuf> buf(IOBuf::create(8));
700     Appender app(buf.get(), 0);
701     app.push(reinterpret_cast<const uint8_t*>("hello"), 5);
702     std::unique_ptr<IOBuf> chain(IOBuf::create(8));
703     chain->prependChain(std::move(buf));
704
705     Cursor curs(chain.get());
706     EXPECT_THROW(curs.readTerminatedString(),
707                  std::out_of_range);
708   }
709
710   // Test reading a null-terminated string past the maximum length
711   {
712     std::unique_ptr<IOBuf> buf(IOBuf::create(8));
713     Appender app(buf.get(), 0);
714     app.push(reinterpret_cast<const uint8_t*>("hello\0"), 6);
715     std::unique_ptr<IOBuf> chain(IOBuf::create(8));
716     chain->prependChain(std::move(buf));
717
718     Cursor curs(chain.get());
719     EXPECT_THROW(curs.readTerminatedString('\0', 3),
720                  std::length_error);
721   }
722
723   // Test reading a two fixed-length strings from a single buffer with an extra
724   // uint8_t at the end
725   {
726     std::unique_ptr<IOBuf> chain(IOBuf::create(16));
727     Appender app(chain.get(), 0);
728     app.push(reinterpret_cast<const uint8_t*>("helloworld\x01"), 11);
729
730     Cursor curs(chain.get());
731     EXPECT_STREQ("hello", curs.readFixedString(5).c_str());
732     EXPECT_STREQ("world", curs.readFixedString(5).c_str());
733     EXPECT_EQ(1, curs.read<uint8_t>());
734   }
735
736   // Test multiple buffers where the first is empty and a fixed-length string
737   // starts in the second buffer.
738   {
739     std::unique_ptr<IOBuf> chain(IOBuf::create(8));
740     chain->prependChain(IOBuf::create(16));
741     Appender app(chain.get(), 0);
742     app.push(reinterpret_cast<const uint8_t*>("hello world"), 11);
743
744     Cursor curs(chain.get());
745     EXPECT_STREQ("hello world", curs.readFixedString(11).c_str());
746   }
747
748   // Test multiple buffers with a single fixed-length string spanning them
749   {
750     std::unique_ptr<IOBuf> chain(IOBuf::create(8));
751     chain->prependChain(IOBuf::create(8));
752     chain->append(7);
753     chain->next()->append(4);
754     RWPrivateCursor rwc(chain.get());
755     rwc.push(reinterpret_cast<const uint8_t*>("hello world"), 11);
756
757     Cursor curs(chain.get());
758     EXPECT_STREQ("hello world", curs.readFixedString(11).c_str());
759   }
760
761   // Test reading a fixed-length string from a chain with an empty buffer at
762   // the front
763   {
764     std::unique_ptr<IOBuf> buf(IOBuf::create(8));
765     Appender app(buf.get(), 0);
766     app.push(reinterpret_cast<const uint8_t*>("hello"), 5);
767     std::unique_ptr<IOBuf> chain(IOBuf::create(8));
768     chain->prependChain(std::move(buf));
769
770     Cursor curs(chain.get());
771     EXPECT_STREQ("hello", curs.readFixedString(5).c_str());
772   }
773 }