add Cursor::readWhile() and skipWhile()
[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
23 #include <gtest/gtest.h>
24
25 using folly::ByteRange;
26 using folly::format;
27 using folly::IOBuf;
28 using folly::StringPiece;
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   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, StringPiece data) {
178   appender.push(ByteRange(data));
179 }
180
181 std::string toString(const IOBuf& buf) {
182   std::string str;
183   Cursor cursor(&buf);
184   ByteRange b;
185   while (!(b = cursor.peekBytes()).empty()) {
186     str.append(reinterpret_cast<const char*>(b.data()), b.size());
187     cursor.skip(b.size());
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 b = cursor.peekBytes();
221     EXPECT_EQ("he", StringPiece(b));
222     cursor.skip(b.size());
223     b = cursor.peekBytes();
224     EXPECT_EQ("llo ", StringPiece(b));
225     cursor.skip(b.size());
226     b = cursor.peekBytes();
227     EXPECT_EQ("world", StringPiece(b));
228     cursor.skip(b.size());
229     EXPECT_EQ(3, iobuf1->countChainElements());
230     EXPECT_EQ(11, iobuf1->computeChainDataLength());
231   }
232
233   {
234     RWPrivateCursor cursor(iobuf1.get());
235     cursor.gather(11);
236     auto b = cursor.peekBytes();
237     EXPECT_EQ("hello world", StringPiece(b));
238     EXPECT_EQ(1, iobuf1->countChainElements());
239     EXPECT_EQ(11, iobuf1->computeChainDataLength());
240   }
241 }
242
243 TEST(IOBuf, pushCursorData) {
244   unique_ptr<IOBuf> iobuf1(IOBuf::create(20));
245   iobuf1->append(15);
246   iobuf1->trimStart(5);
247   unique_ptr<IOBuf> iobuf2(IOBuf::create(10));
248   unique_ptr<IOBuf> iobuf3(IOBuf::create(10));
249   iobuf3->append(10);
250
251   iobuf1->prependChain(std::move(iobuf2));
252   iobuf1->prependChain(std::move(iobuf3));
253   EXPECT_TRUE(iobuf1->isChained());
254
255   //write 20 bytes to the buffer chain
256   RWPrivateCursor wcursor(iobuf1.get());
257   EXPECT_FALSE(wcursor.isAtEnd());
258   wcursor.writeBE<uint64_t>(1);
259   wcursor.writeBE<uint64_t>(10);
260   wcursor.writeBE<uint32_t>(20);
261   EXPECT_TRUE(wcursor.isAtEnd());
262
263   // create a read buffer for the buffer chain
264   Cursor rcursor(iobuf1.get());
265   EXPECT_EQ(1, rcursor.readBE<uint64_t>());
266   EXPECT_EQ(10, rcursor.readBE<uint64_t>());
267   EXPECT_EQ(20, rcursor.readBE<uint32_t>());
268   EXPECT_EQ(0, rcursor.totalLength());
269   rcursor.reset(iobuf1.get());
270   EXPECT_EQ(20, rcursor.totalLength());
271
272   // create another write buffer
273   unique_ptr<IOBuf> iobuf4(IOBuf::create(30));
274   iobuf4->append(30);
275   RWPrivateCursor wcursor2(iobuf4.get());
276   // write buffer chain data into it, now wcursor2 should only
277   // have 10 bytes writable space
278   wcursor2.push(rcursor, 20);
279   EXPECT_EQ(wcursor2.totalLength(), 10);
280   // write again with not enough space in rcursor
281   EXPECT_THROW(wcursor2.push(rcursor, 20), std::out_of_range);
282
283   // create a read cursor to check iobuf3 data back
284   Cursor rcursor2(iobuf4.get());
285   EXPECT_EQ(1, rcursor2.readBE<uint64_t>());
286   EXPECT_EQ(10, rcursor2.readBE<uint64_t>());
287   EXPECT_EQ(20, rcursor2.readBE<uint32_t>());
288
289 }
290
291 TEST(IOBuf, Gather) {
292   std::unique_ptr<IOBuf> iobuf1(IOBuf::create(10));
293   append(iobuf1, "he");
294   std::unique_ptr<IOBuf> iobuf2(IOBuf::create(10));
295   append(iobuf2, "llo ");
296   std::unique_ptr<IOBuf> iobuf3(IOBuf::create(10));
297   append(iobuf3, "world");
298   iobuf1->prependChain(std::move(iobuf2));
299   iobuf1->prependChain(std::move(iobuf3));
300   EXPECT_EQ(3, iobuf1->countChainElements());
301   EXPECT_EQ(11, iobuf1->computeChainDataLength());
302
303   // Attempting to gather() more data than available in the chain should fail.
304   // Try from the very beginning of the chain.
305   RWPrivateCursor cursor(iobuf1.get());
306   EXPECT_THROW(cursor.gather(15), std::overflow_error);
307   // Now try from the middle of the chain
308   cursor += 3;
309   EXPECT_THROW(cursor.gather(10), std::overflow_error);
310
311   // Calling gatherAtMost() should succeed, however, and just gather
312   // as much as it can
313   cursor.gatherAtMost(10);
314   EXPECT_EQ(8, cursor.length());
315   EXPECT_EQ(8, cursor.totalLength());
316   EXPECT_FALSE(cursor.isAtEnd());
317   EXPECT_EQ("lo world",
318             folly::StringPiece(reinterpret_cast<const char*>(cursor.data()),
319                                cursor.length()));
320   EXPECT_EQ(2, iobuf1->countChainElements());
321   EXPECT_EQ(11, iobuf1->computeChainDataLength());
322
323   // Now try gather again on the chain head
324   cursor = RWPrivateCursor(iobuf1.get());
325   cursor.gather(5);
326   // Since gather() doesn't split buffers, everything should be collapsed into
327   // a single buffer now.
328   EXPECT_EQ(1, iobuf1->countChainElements());
329   EXPECT_EQ(11, iobuf1->computeChainDataLength());
330   EXPECT_EQ(11, cursor.length());
331   EXPECT_EQ(11, cursor.totalLength());
332 }
333
334 TEST(IOBuf, cloneAndInsert) {
335   std::unique_ptr<IOBuf> iobuf1(IOBuf::create(10));
336   append(iobuf1, "he");
337   std::unique_ptr<IOBuf> iobuf2(IOBuf::create(10));
338   append(iobuf2, "llo ");
339   std::unique_ptr<IOBuf> iobuf3(IOBuf::create(10));
340   append(iobuf3, "world");
341   iobuf1->prependChain(std::move(iobuf2));
342   iobuf1->prependChain(std::move(iobuf3));
343   EXPECT_EQ(3, iobuf1->countChainElements());
344   EXPECT_EQ(11, iobuf1->computeChainDataLength());
345
346   std::unique_ptr<IOBuf> cloned;
347
348   Cursor(iobuf1.get()).clone(cloned, 3);
349   EXPECT_EQ(2, cloned->countChainElements());
350   EXPECT_EQ(3, cloned->computeChainDataLength());
351
352
353   EXPECT_EQ(11, Cursor(iobuf1.get()).cloneAtMost(cloned, 20));
354   EXPECT_EQ(3, cloned->countChainElements());
355   EXPECT_EQ(11, cloned->computeChainDataLength());
356
357
358   EXPECT_THROW({Cursor(iobuf1.get()).clone(cloned, 20);},
359                std::out_of_range);
360
361   {
362     // Check that inserting in the middle of an iobuf splits
363     RWPrivateCursor cursor(iobuf1.get());
364     Cursor(iobuf1.get()).clone(cloned, 3);
365     EXPECT_EQ(2, cloned->countChainElements());
366     EXPECT_EQ(3, cloned->computeChainDataLength());
367
368     cursor.skip(1);
369
370     cursor.insert(std::move(cloned));
371     cursor.insert(folly::IOBuf::create(0));
372     EXPECT_EQ(7, iobuf1->countChainElements());
373     EXPECT_EQ(14, iobuf1->computeChainDataLength());
374     // Check that nextBuf got set correctly to the buffer with 1 byte left
375     EXPECT_EQ(1, cursor.peekBytes().size());
376     cursor.read<uint8_t>();
377   }
378
379   {
380     // Check that inserting at the end doesn't create empty buf
381     RWPrivateCursor cursor(iobuf1.get());
382     Cursor(iobuf1.get()).clone(cloned, 1);
383     EXPECT_EQ(1, cloned->countChainElements());
384     EXPECT_EQ(1, cloned->computeChainDataLength());
385
386     cursor.skip(1);
387
388     cursor.insert(std::move(cloned));
389     EXPECT_EQ(8, iobuf1->countChainElements());
390     EXPECT_EQ(15, iobuf1->computeChainDataLength());
391     // Check that nextBuf got set correctly
392     cursor.read<uint8_t>();
393   }
394   {
395     // Check that inserting at the beginning doesn't create empty buf
396     RWPrivateCursor cursor(iobuf1.get());
397     Cursor(iobuf1.get()).clone(cloned, 1);
398     EXPECT_EQ(1, cloned->countChainElements());
399     EXPECT_EQ(1, cloned->computeChainDataLength());
400
401     cursor.insert(std::move(cloned));
402     EXPECT_EQ(9, iobuf1->countChainElements());
403     EXPECT_EQ(16, iobuf1->computeChainDataLength());
404     // Check that nextBuf got set correctly
405     cursor.read<uint8_t>();
406   }
407 }
408
409 TEST(IOBuf, cloneWithEmptyBufAtStart) {
410   folly::IOBufEqual eq;
411   auto empty = IOBuf::create(0);
412   auto hel = IOBuf::create(3);
413   append(hel, "hel");
414   auto lo = IOBuf::create(2);
415   append(lo, "lo");
416
417   auto iobuf = empty->clone();
418   iobuf->prependChain(hel->clone());
419   iobuf->prependChain(lo->clone());
420   iobuf->prependChain(empty->clone());
421   iobuf->prependChain(hel->clone());
422   iobuf->prependChain(lo->clone());
423   iobuf->prependChain(empty->clone());
424   iobuf->prependChain(lo->clone());
425   iobuf->prependChain(hel->clone());
426   iobuf->prependChain(lo->clone());
427   iobuf->prependChain(lo->clone());
428
429   Cursor cursor(iobuf.get());
430   std::unique_ptr<IOBuf> cloned;
431   char data[3];
432   cursor.pull(&data, 3);
433   cursor.clone(cloned, 2);
434   EXPECT_EQ(1, cloned->countChainElements());
435   EXPECT_EQ(2, cloned->length());
436   EXPECT_TRUE(eq(lo, cloned));
437
438   cursor.pull(&data, 3);
439   EXPECT_EQ("hel", std::string(data, sizeof(data)));
440
441   cursor.skip(2);
442   cursor.clone(cloned, 2);
443   EXPECT_TRUE(eq(lo, cloned));
444
445   std::string hello = cursor.readFixedString(5);
446   cursor.clone(cloned, 2);
447   EXPECT_TRUE(eq(lo, cloned));
448 }
449
450 TEST(IOBuf, Appender) {
451   std::unique_ptr<IOBuf> head(IOBuf::create(10));
452   append(head, "hello");
453
454   Appender app(head.get(), 10);
455   uint32_t cap = head->capacity();
456   uint32_t len1 = app.length();
457   EXPECT_EQ(cap - 5, len1);
458   app.ensure(len1);  // won't grow
459   EXPECT_EQ(len1, app.length());
460   app.ensure(len1 + 1);  // will grow
461   EXPECT_LE(len1 + 1, app.length());
462
463   append(app, " world");
464   EXPECT_EQ("hello world", toString(*head));
465 }
466
467 TEST(IOBuf, Printf) {
468   IOBuf head(IOBuf::CREATE, 24);
469   Appender app(&head, 32);
470
471   app.printf("%s", "test");
472   EXPECT_EQ(head.length(), 4);
473   EXPECT_EQ(0, memcmp(head.data(), "test\0", 5));
474
475   app.printf("%d%s %s%s %#x", 32, "this string is",
476              "longer than our original allocation size,",
477              "and will therefore require a new allocation", 0x12345678);
478   // The tailroom should start with a nul byte now.
479   EXPECT_GE(head.prev()->tailroom(), 1);
480   EXPECT_EQ(0, *head.prev()->tail());
481
482   EXPECT_EQ("test32this string is longer than our original "
483             "allocation size,and will therefore require a "
484             "new allocation 0x12345678",
485             head.moveToFbString().toStdString());
486 }
487
488 TEST(IOBuf, Format) {
489   IOBuf head(IOBuf::CREATE, 24);
490   Appender app(&head, 32);
491
492   format("{}", "test")(app);
493   EXPECT_EQ(head.length(), 4);
494   EXPECT_EQ(0, memcmp(head.data(), "test", 4));
495
496   auto fmt = format("{}{} {}{} {:#x}",
497                     32, "this string is",
498                     "longer than our original allocation size,",
499                     "and will therefore require a new allocation",
500                     0x12345678);
501   fmt(app);
502   EXPECT_EQ("test32this string is longer than our original "
503             "allocation size,and will therefore require a "
504             "new allocation 0x12345678",
505             head.moveToFbString().toStdString());
506 }
507
508 TEST(IOBuf, QueueAppender) {
509   folly::IOBufQueue queue;
510
511   // Allocate 100 bytes at once, but don't grow past 1024
512   QueueAppender app(&queue, 100);
513   size_t n = 1024 / sizeof(uint32_t);
514   for (uint32_t i = 0; i < n; ++i) {
515     app.writeBE(i);
516   }
517
518   // There must be a goodMallocSize between 100 and 1024...
519   EXPECT_LT(1, queue.front()->countChainElements());
520   const IOBuf* buf = queue.front();
521   do {
522     EXPECT_LE(100, buf->capacity());
523     buf = buf->next();
524   } while (buf != queue.front());
525
526   Cursor cursor(queue.front());
527   for (uint32_t i = 0; i < n; ++i) {
528     EXPECT_EQ(i, cursor.readBE<uint32_t>());
529   }
530
531   EXPECT_THROW({cursor.readBE<uint32_t>();}, std::out_of_range);
532 }
533
534 TEST(IOBuf, CursorOperators) {
535   // Test operators on a single-item chain
536   {
537     std::unique_ptr<IOBuf> chain1(IOBuf::create(20));
538     chain1->append(10);
539
540     Cursor curs1(chain1.get());
541     EXPECT_EQ(0, curs1 - chain1.get());
542     EXPECT_FALSE(curs1.isAtEnd());
543     curs1.skip(3);
544     EXPECT_EQ(3, curs1 - chain1.get());
545     EXPECT_FALSE(curs1.isAtEnd());
546     curs1.skip(7);
547     EXPECT_EQ(10, curs1 - chain1.get());
548     EXPECT_TRUE(curs1.isAtEnd());
549
550     Cursor curs2(chain1.get());
551     EXPECT_EQ(0, curs2 - chain1.get());
552     EXPECT_EQ(10, curs1 - curs2);
553     EXPECT_THROW(curs2 - curs1, std::out_of_range);
554   }
555
556   // Test cross-chain operations
557   {
558     std::unique_ptr<IOBuf> chain1(IOBuf::create(20));
559     chain1->append(10);
560     std::unique_ptr<IOBuf> chain2 = chain1->clone();
561
562     Cursor curs1(chain1.get());
563     Cursor curs2(chain2.get());
564     EXPECT_THROW(curs1 - curs2, std::out_of_range);
565     EXPECT_THROW(curs1 - chain2.get(), std::out_of_range);
566   }
567
568   // Test operations on multi-item chains
569   {
570     std::unique_ptr<IOBuf> chain(IOBuf::create(20));
571     chain->append(10);
572     chain->appendChain(chain->clone());
573     EXPECT_EQ(20, chain->computeChainDataLength());
574
575     Cursor curs1(chain.get());
576     curs1.skip(5);
577     Cursor curs2(chain.get());
578     curs2.skip(3);
579     EXPECT_EQ(2, curs1 - curs2);
580     EXPECT_EQ(5, curs1 - chain.get());
581     EXPECT_THROW(curs2 - curs1, std::out_of_range);
582
583     curs1.skip(7);
584     EXPECT_EQ(9, curs1 - curs2);
585     EXPECT_EQ(12, curs1 - chain.get());
586     EXPECT_THROW(curs2 - curs1, std::out_of_range);
587
588     curs2.skip(7);
589     EXPECT_EQ(2, curs1 - curs2);
590     EXPECT_THROW(curs2 - curs1, std::out_of_range);
591   }
592
593   // Test isAtEnd() with empty buffers at the end of a chain
594   {
595     auto iobuf1 = IOBuf::create(20);
596     iobuf1->append(15);
597     iobuf1->trimStart(5);
598
599     Cursor c(iobuf1.get());
600     EXPECT_FALSE(c.isAtEnd());
601     c.skip(10);
602     EXPECT_TRUE(c.isAtEnd());
603
604     iobuf1->prependChain(IOBuf::create(10));
605     iobuf1->prependChain(IOBuf::create(10));
606     EXPECT_TRUE(c.isAtEnd());
607     iobuf1->prev()->append(5);
608     EXPECT_FALSE(c.isAtEnd());
609     c.skip(5);
610     EXPECT_TRUE(c.isAtEnd());
611   }
612
613   // Test canAdvance with a chain of items
614   {
615     auto chain = IOBuf::create(10);
616     chain->append(10);
617     chain->appendChain(chain->clone());
618     EXPECT_EQ(2, chain->countChainElements());
619     EXPECT_EQ(20, chain->computeChainDataLength());
620
621     Cursor c(chain.get());
622     for (size_t i = 0; i <= 20; ++i) {
623       EXPECT_TRUE(c.canAdvance(i));
624     }
625     EXPECT_FALSE(c.canAdvance(21));
626     c.skip(10);
627     EXPECT_TRUE(c.canAdvance(10));
628     EXPECT_FALSE(c.canAdvance(11));
629   }
630 }
631
632 TEST(IOBuf, StringOperations) {
633   // Test a single buffer with two null-terminated strings and an extra uint8_t
634   // at the end
635   {
636     std::unique_ptr<IOBuf> chain(IOBuf::create(16));
637     Appender app(chain.get(), 0);
638     app.push(reinterpret_cast<const uint8_t*>("hello\0world\0\x01"), 13);
639
640     Cursor curs(chain.get());
641     EXPECT_STREQ("hello", curs.readTerminatedString().c_str());
642     EXPECT_STREQ("world", curs.readTerminatedString().c_str());
643     EXPECT_EQ(1, curs.read<uint8_t>());
644   }
645
646   // Test multiple buffers where the first is empty and the string starts in
647   // the second buffer.
648   {
649     std::unique_ptr<IOBuf> chain(IOBuf::create(8));
650     chain->prependChain(IOBuf::create(12));
651     Appender app(chain.get(), 0);
652     app.push(reinterpret_cast<const uint8_t*>("hello world\0"), 12);
653
654     Cursor curs(chain.get());
655     EXPECT_STREQ("hello world", curs.readTerminatedString().c_str());
656   }
657
658   // Test multiple buffers with a single null-terminated string spanning them
659   {
660     std::unique_ptr<IOBuf> chain(IOBuf::create(8));
661     chain->prependChain(IOBuf::create(8));
662     chain->append(8);
663     chain->next()->append(4);
664     RWPrivateCursor rwc(chain.get());
665     rwc.push(reinterpret_cast<const uint8_t*>("hello world\0"), 12);
666
667     Cursor curs(chain.get());
668     EXPECT_STREQ("hello world", curs.readTerminatedString().c_str());
669   }
670
671   // Test a reading a null-terminated string that's longer than the maximum
672   // allowable length
673   {
674     std::unique_ptr<IOBuf> chain(IOBuf::create(16));
675     Appender app(chain.get(), 0);
676     app.push(reinterpret_cast<const uint8_t*>("hello world\0"), 12);
677
678     Cursor curs(chain.get());
679     EXPECT_THROW(curs.readTerminatedString('\0', 5), std::length_error);
680   }
681
682   // Test reading a null-terminated string from a chain with an empty buffer at
683   // the front
684   {
685     std::unique_ptr<IOBuf> buf(IOBuf::create(8));
686     Appender app(buf.get(), 0);
687     app.push(reinterpret_cast<const uint8_t*>("hello\0"), 6);
688     std::unique_ptr<IOBuf> chain(IOBuf::create(8));
689     chain->prependChain(std::move(buf));
690
691     Cursor curs(chain.get());
692     EXPECT_STREQ("hello", curs.readTerminatedString().c_str());
693   }
694
695   // Test reading a null-terminated string from a chain that doesn't contain the
696   // terminator
697   {
698     std::unique_ptr<IOBuf> buf(IOBuf::create(8));
699     Appender app(buf.get(), 0);
700     app.push(reinterpret_cast<const uint8_t*>("hello"), 5);
701     std::unique_ptr<IOBuf> chain(IOBuf::create(8));
702     chain->prependChain(std::move(buf));
703
704     Cursor curs(chain.get());
705     EXPECT_THROW(curs.readTerminatedString(),
706                  std::out_of_range);
707   }
708
709   // Test reading a null-terminated string past the maximum length
710   {
711     std::unique_ptr<IOBuf> buf(IOBuf::create(8));
712     Appender app(buf.get(), 0);
713     app.push(reinterpret_cast<const uint8_t*>("hello\0"), 6);
714     std::unique_ptr<IOBuf> chain(IOBuf::create(8));
715     chain->prependChain(std::move(buf));
716
717     Cursor curs(chain.get());
718     EXPECT_THROW(curs.readTerminatedString('\0', 3),
719                  std::length_error);
720   }
721
722   // Test reading a two fixed-length strings from a single buffer with an extra
723   // uint8_t at the end
724   {
725     std::unique_ptr<IOBuf> chain(IOBuf::create(16));
726     Appender app(chain.get(), 0);
727     app.push(reinterpret_cast<const uint8_t*>("helloworld\x01"), 11);
728
729     Cursor curs(chain.get());
730     EXPECT_STREQ("hello", curs.readFixedString(5).c_str());
731     EXPECT_STREQ("world", curs.readFixedString(5).c_str());
732     EXPECT_EQ(1, curs.read<uint8_t>());
733   }
734
735   // Test multiple buffers where the first is empty and a fixed-length string
736   // starts in the second buffer.
737   {
738     std::unique_ptr<IOBuf> chain(IOBuf::create(8));
739     chain->prependChain(IOBuf::create(16));
740     Appender app(chain.get(), 0);
741     app.push(reinterpret_cast<const uint8_t*>("hello world"), 11);
742
743     Cursor curs(chain.get());
744     EXPECT_STREQ("hello world", curs.readFixedString(11).c_str());
745   }
746
747   // Test multiple buffers with a single fixed-length string spanning them
748   {
749     std::unique_ptr<IOBuf> chain(IOBuf::create(8));
750     chain->prependChain(IOBuf::create(8));
751     chain->append(7);
752     chain->next()->append(4);
753     RWPrivateCursor rwc(chain.get());
754     rwc.push(reinterpret_cast<const uint8_t*>("hello world"), 11);
755
756     Cursor curs(chain.get());
757     EXPECT_STREQ("hello world", curs.readFixedString(11).c_str());
758   }
759
760   // Test reading a fixed-length string from a chain with an empty buffer at
761   // the front
762   {
763     std::unique_ptr<IOBuf> buf(IOBuf::create(8));
764     Appender app(buf.get(), 0);
765     app.push(reinterpret_cast<const uint8_t*>("hello"), 5);
766     std::unique_ptr<IOBuf> chain(IOBuf::create(8));
767     chain->prependChain(std::move(buf));
768
769     Cursor curs(chain.get());
770     EXPECT_STREQ("hello", curs.readFixedString(5).c_str());
771   }
772 }
773
774 TEST(IOBuf, ReadWhileTrue) {
775   auto isAlpha = [](uint8_t ch) {
776     return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z');
777   };
778   auto isDigit = [](uint8_t ch) { return (ch >= '0' && ch <= '9'); };
779
780   // Test reading alternating alphabetic and numeric strings
781   {
782     std::unique_ptr<IOBuf> chain(IOBuf::create(32));
783     Appender app(chain.get(), 0);
784     app.push(StringPiece("hello123world456"));
785
786     Cursor curs(chain.get());
787     EXPECT_STREQ("hello", curs.readWhile(isAlpha).c_str());
788     EXPECT_STREQ("123", curs.readWhile(isDigit).c_str());
789     EXPECT_STREQ("world", curs.readWhile(isAlpha).c_str());
790     EXPECT_STREQ("456", curs.readWhile(isDigit).c_str());
791     EXPECT_TRUE(curs.isAtEnd());
792   }
793
794   // The same, but also use skipWhile()
795   {
796     std::unique_ptr<IOBuf> chain(IOBuf::create(16));
797     Appender app(chain.get(), 0);
798     app.push(StringPiece("hello123world456"));
799
800     Cursor curs(chain.get());
801     EXPECT_STREQ("hello", curs.readWhile(isAlpha).c_str());
802     curs.skipWhile(isDigit);
803     curs.skipWhile(isAlpha);
804     EXPECT_STREQ("456", curs.readWhile(isDigit).c_str());
805     EXPECT_TRUE(curs.isAtEnd());
806   }
807
808   // Test readWhile() using data split across multiple buffers,
809   // including some empty buffers in the middle of the chain.
810   {
811     std::unique_ptr<IOBuf> chain;
812
813     // First element in the chain has "he"
814     auto buf = IOBuf::create(40);
815     Appender app(buf.get(), 0);
816     app.push(StringPiece("he"));
817     chain = std::move(buf);
818
819     // The second element has "ll", after 10 bytes of headroom
820     buf = IOBuf::create(40);
821     buf->advance(10);
822     app = Appender{buf.get(), 0};
823     app.push(StringPiece("ll"));
824     chain->prependChain(std::move(buf));
825
826     // The third element is empty
827     buf = IOBuf::create(40);
828     buf->advance(15);
829     chain->prependChain(std::move(buf));
830
831     // The fourth element has "o12"
832     buf = IOBuf::create(40);
833     buf->advance(37);
834     app = Appender{buf.get(), 0};
835     app.push(StringPiece("o12"));
836     chain->prependChain(std::move(buf));
837
838     // The fifth element has "3"
839     buf = IOBuf::create(40);
840     app = Appender{buf.get(), 0};
841     app.push(StringPiece("3"));
842     chain->prependChain(std::move(buf));
843
844     // The sixth element is empty
845     buf = IOBuf::create(40);
846     chain->prependChain(std::move(buf));
847
848     // The seventh element has "world456"
849     buf = IOBuf::create(40);
850     app = Appender{buf.get(), 0};
851     app.push(StringPiece("world456"));
852     chain->prependChain(std::move(buf));
853
854     // The eighth element is empty
855     buf = IOBuf::create(40);
856     chain->prependChain(std::move(buf));
857
858     Cursor curs(chain.get());
859     EXPECT_STREQ("hello", curs.readWhile(isAlpha).c_str());
860     EXPECT_STREQ("123", curs.readWhile(isDigit).c_str());
861     EXPECT_STREQ("world", curs.readWhile(isAlpha).c_str());
862     EXPECT_STREQ("456", curs.readWhile(isDigit).c_str());
863     EXPECT_TRUE(curs.isAtEnd());
864   }
865 }