2 * Copyright 2017 Facebook, Inc.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 #include <folly/io/IOBuf.h>
19 #include <folly/Format.h>
20 #include <folly/Range.h>
21 #include <folly/io/Cursor.h>
22 #include <folly/portability/GTest.h>
24 using folly::ByteRange;
27 using folly::StringPiece;
28 using std::unique_ptr;
29 using namespace folly::io;
31 TEST(IOBuf, RWCursor) {
32 unique_ptr<IOBuf> iobuf1(IOBuf::create(20));
34 unique_ptr<IOBuf> iobuf2(IOBuf::create(20));
38 iobuf1->prependChain(std::move(iobuf2));
40 EXPECT_TRUE(iobuf1->isChained());
42 RWPrivateCursor wcursor(iobuf1.get());
43 Cursor rcursor(iobuf1.get());
44 wcursor.writeLE((uint64_t)1);
45 wcursor.writeLE((uint64_t)1);
46 wcursor.writeLE((uint64_t)1);
47 wcursor.write((uint8_t)1);
49 EXPECT_EQ(1u, rcursor.readLE<uint64_t>());
51 EXPECT_EQ(1u, rcursor.readLE<uint32_t>());
53 EXPECT_EQ(0u, rcursor.read<uint8_t>());
54 EXPECT_EQ(0u, rcursor.read<uint8_t>());
55 EXPECT_EQ(0u, rcursor.read<uint8_t>());
56 EXPECT_EQ(0u, rcursor.read<uint8_t>());
57 EXPECT_EQ(1u, rcursor.read<uint8_t>());
61 unique_ptr<IOBuf> iobuf1(IOBuf::create(20));
63 RWPrivateCursor wcursor(iobuf1.get());
64 wcursor.write((uint8_t)1);
65 wcursor.write((uint8_t)2);
66 Cursor cursor(iobuf1.get());
68 EXPECT_EQ(2, cursor.read<uint8_t>());
72 unique_ptr<IOBuf> iobuf1(IOBuf::create(20));
74 RWPrivateCursor wcursor(iobuf1.get());
75 wcursor.write((uint8_t)1);
76 wcursor.write((uint8_t)2);
77 wcursor.reset(iobuf1.get());
78 EXPECT_EQ(1, wcursor.read<uint8_t>());
81 TEST(IOBuf, copy_assign_convert) {
82 unique_ptr<IOBuf> iobuf1(IOBuf::create(20));
84 RWPrivateCursor wcursor(iobuf1.get());
85 RWPrivateCursor cursor2(wcursor);
86 RWPrivateCursor cursor3(iobuf1.get());
88 wcursor.write((uint8_t)1);
90 wcursor.write((uint8_t)2);
91 Cursor cursor4(wcursor);
92 RWPrivateCursor cursor5(wcursor);
93 wcursor.write((uint8_t)3);
95 EXPECT_EQ(1, cursor2.read<uint8_t>());
96 EXPECT_EQ(2, cursor3.read<uint8_t>());
97 EXPECT_EQ(3, cursor4.read<uint8_t>());
100 TEST(IOBuf, arithmetic) {
101 IOBuf iobuf1(IOBuf::CREATE, 20);
103 RWPrivateCursor wcursor(&iobuf1);
105 wcursor.write((uint8_t)1);
106 Cursor cursor(&iobuf1);
108 EXPECT_EQ(1, cursor.read<uint8_t>());
110 Cursor start(&iobuf1);
111 Cursor cursor2 = start + 9;
112 EXPECT_EQ(7, cursor2 - cursor);
113 EXPECT_NE(cursor, cursor2);
115 cursor2 = cursor2 + 1;
116 EXPECT_EQ(cursor, cursor2);
119 TEST(IOBuf, endian) {
120 unique_ptr<IOBuf> iobuf1(IOBuf::create(20));
122 RWPrivateCursor wcursor(iobuf1.get());
123 Cursor rcursor(iobuf1.get());
128 // Try a couple combinations to ensure they were generated correctly
133 EXPECT_EQ(v, rcursor.readBE<uint16_t>());
136 TEST(IOBuf, Cursor) {
137 unique_ptr<IOBuf> iobuf1(IOBuf::create(1));
139 RWPrivateCursor c(iobuf1.get());
140 c.write((uint8_t)40); // OK
142 c.write((uint8_t)10); // Bad write, checked should except.
143 EXPECT_EQ(true, false);
148 TEST(IOBuf, UnshareCursor) {
150 unique_ptr<IOBuf> iobuf1(IOBuf::wrapBuffer(&buf, 1));
151 unique_ptr<IOBuf> iobuf2(IOBuf::wrapBuffer(&buf, 1));
152 RWUnshareCursor c1(iobuf1.get());
153 RWUnshareCursor c2(iobuf2.get());
155 c1.write((uint8_t)10); // This should duplicate the two buffers.
156 uint8_t t = c2.read<uint8_t>();
159 iobuf1 = IOBuf::wrapBuffer(&buf, 1);
160 iobuf2 = IOBuf::wrapBuffer(&buf, 1);
161 RWPrivateCursor c3(iobuf1.get());
162 RWPrivateCursor c4(iobuf2.get());
164 c3.write((uint8_t)10); // This should _not_ duplicate the two buffers.
165 t = c4.read<uint8_t>();
170 void append(std::unique_ptr<IOBuf>& buf, folly::StringPiece data) {
171 EXPECT_LE(data.size(), buf->tailroom());
172 memcpy(buf->writableData(), data.data(), data.size());
173 buf->append(data.size());
176 void append(Appender& appender, StringPiece data) {
177 appender.push(ByteRange(data));
180 std::string toString(const IOBuf& buf) {
184 while (!(b = cursor.peekBytes()).empty()) {
185 str.append(reinterpret_cast<const char*>(b.data()), b.size());
186 cursor.skip(b.size());
193 TEST(IOBuf, PullAndPeek) {
194 std::unique_ptr<IOBuf> iobuf1(IOBuf::create(10));
195 append(iobuf1, "he");
196 std::unique_ptr<IOBuf> iobuf2(IOBuf::create(10));
197 append(iobuf2, "llo ");
198 std::unique_ptr<IOBuf> iobuf3(IOBuf::create(10));
199 append(iobuf3, "world");
200 iobuf1->prependChain(std::move(iobuf2));
201 iobuf1->prependChain(std::move(iobuf3));
202 EXPECT_EQ(3, iobuf1->countChainElements());
203 EXPECT_EQ(11, iobuf1->computeChainDataLength());
206 memset(buf, 0, sizeof(buf));
207 Cursor(iobuf1.get()).pull(buf, 11);
208 EXPECT_EQ("hello world", std::string(buf));
210 memset(buf, 0, sizeof(buf));
211 EXPECT_EQ(11, Cursor(iobuf1.get()).pullAtMost(buf, 20));
212 EXPECT_EQ("hello world", std::string(buf));
214 EXPECT_THROW({Cursor(iobuf1.get()).pull(buf, 20);},
218 RWPrivateCursor cursor(iobuf1.get());
219 auto b = cursor.peekBytes();
220 EXPECT_EQ("he", StringPiece(b));
221 cursor.skip(b.size());
222 b = cursor.peekBytes();
223 EXPECT_EQ("llo ", StringPiece(b));
224 cursor.skip(b.size());
225 b = cursor.peekBytes();
226 EXPECT_EQ("world", StringPiece(b));
227 cursor.skip(b.size());
228 EXPECT_EQ(3, iobuf1->countChainElements());
229 EXPECT_EQ(11, iobuf1->computeChainDataLength());
233 RWPrivateCursor cursor(iobuf1.get());
235 auto b = cursor.peekBytes();
236 EXPECT_EQ("hello world", StringPiece(b));
237 EXPECT_EQ(1, iobuf1->countChainElements());
238 EXPECT_EQ(11, iobuf1->computeChainDataLength());
242 TEST(IOBuf, pushCursorData) {
243 unique_ptr<IOBuf> iobuf1(IOBuf::create(20));
245 iobuf1->trimStart(5);
246 unique_ptr<IOBuf> iobuf2(IOBuf::create(10));
247 unique_ptr<IOBuf> iobuf3(IOBuf::create(10));
250 iobuf1->prependChain(std::move(iobuf2));
251 iobuf1->prependChain(std::move(iobuf3));
252 EXPECT_TRUE(iobuf1->isChained());
254 //write 20 bytes to the buffer chain
255 RWPrivateCursor wcursor(iobuf1.get());
256 EXPECT_FALSE(wcursor.isAtEnd());
257 wcursor.writeBE<uint64_t>(1);
258 wcursor.writeBE<uint64_t>(10);
259 wcursor.writeBE<uint32_t>(20);
260 EXPECT_TRUE(wcursor.isAtEnd());
262 // create a read buffer for the buffer chain
263 Cursor rcursor(iobuf1.get());
264 EXPECT_EQ(1, rcursor.readBE<uint64_t>());
265 EXPECT_EQ(10, rcursor.readBE<uint64_t>());
266 EXPECT_EQ(20, rcursor.readBE<uint32_t>());
267 EXPECT_EQ(0, rcursor.totalLength());
268 rcursor.reset(iobuf1.get());
269 EXPECT_EQ(20, rcursor.totalLength());
271 // create another write buffer
272 unique_ptr<IOBuf> iobuf4(IOBuf::create(30));
274 RWPrivateCursor wcursor2(iobuf4.get());
275 // write buffer chain data into it, now wcursor2 should only
276 // have 10 bytes writable space
277 wcursor2.push(rcursor, 20);
278 EXPECT_EQ(wcursor2.totalLength(), 10);
279 // write again with not enough space in rcursor
280 EXPECT_THROW(wcursor2.push(rcursor, 20), std::out_of_range);
282 // create a read cursor to check iobuf3 data back
283 Cursor rcursor2(iobuf4.get());
284 EXPECT_EQ(1, rcursor2.readBE<uint64_t>());
285 EXPECT_EQ(10, rcursor2.readBE<uint64_t>());
286 EXPECT_EQ(20, rcursor2.readBE<uint32_t>());
290 TEST(IOBuf, Gather) {
291 std::unique_ptr<IOBuf> iobuf1(IOBuf::create(10));
292 append(iobuf1, "he");
293 std::unique_ptr<IOBuf> iobuf2(IOBuf::create(10));
294 append(iobuf2, "llo ");
295 std::unique_ptr<IOBuf> iobuf3(IOBuf::create(10));
296 append(iobuf3, "world");
297 iobuf1->prependChain(std::move(iobuf2));
298 iobuf1->prependChain(std::move(iobuf3));
299 EXPECT_EQ(3, iobuf1->countChainElements());
300 EXPECT_EQ(11, iobuf1->computeChainDataLength());
302 // Attempting to gather() more data than available in the chain should fail.
303 // Try from the very beginning of the chain.
304 RWPrivateCursor cursor(iobuf1.get());
305 EXPECT_THROW(cursor.gather(15), std::overflow_error);
306 // Now try from the middle of the chain
308 EXPECT_THROW(cursor.gather(10), std::overflow_error);
310 // Calling gatherAtMost() should succeed, however, and just gather
312 cursor.gatherAtMost(10);
313 EXPECT_EQ(8, cursor.length());
314 EXPECT_EQ(8, cursor.totalLength());
315 EXPECT_FALSE(cursor.isAtEnd());
316 EXPECT_EQ("lo world",
317 folly::StringPiece(reinterpret_cast<const char*>(cursor.data()),
319 EXPECT_EQ(2, iobuf1->countChainElements());
320 EXPECT_EQ(11, iobuf1->computeChainDataLength());
322 // Now try gather again on the chain head
323 cursor = RWPrivateCursor(iobuf1.get());
325 // Since gather() doesn't split buffers, everything should be collapsed into
326 // a single buffer now.
327 EXPECT_EQ(1, iobuf1->countChainElements());
328 EXPECT_EQ(11, iobuf1->computeChainDataLength());
329 EXPECT_EQ(11, cursor.length());
330 EXPECT_EQ(11, cursor.totalLength());
333 TEST(IOBuf, cloneAndInsert) {
334 std::unique_ptr<IOBuf> iobuf1(IOBuf::create(10));
335 append(iobuf1, "he");
336 std::unique_ptr<IOBuf> iobuf2(IOBuf::create(10));
337 append(iobuf2, "llo ");
338 std::unique_ptr<IOBuf> iobuf3(IOBuf::create(10));
339 append(iobuf3, "world");
340 iobuf1->prependChain(std::move(iobuf2));
341 iobuf1->prependChain(std::move(iobuf3));
342 EXPECT_EQ(3, iobuf1->countChainElements());
343 EXPECT_EQ(11, iobuf1->computeChainDataLength());
345 std::unique_ptr<IOBuf> cloned;
347 Cursor(iobuf1.get()).clone(cloned, 3);
348 EXPECT_EQ(2, cloned->countChainElements());
349 EXPECT_EQ(3, cloned->computeChainDataLength());
352 EXPECT_EQ(11, Cursor(iobuf1.get()).cloneAtMost(cloned, 20));
353 EXPECT_EQ(3, cloned->countChainElements());
354 EXPECT_EQ(11, cloned->computeChainDataLength());
357 EXPECT_THROW({Cursor(iobuf1.get()).clone(cloned, 20);},
361 // Check that inserting in the middle of an iobuf splits
362 RWPrivateCursor cursor(iobuf1.get());
363 Cursor(iobuf1.get()).clone(cloned, 3);
364 EXPECT_EQ(2, cloned->countChainElements());
365 EXPECT_EQ(3, cloned->computeChainDataLength());
369 cursor.insert(std::move(cloned));
370 cursor.insert(folly::IOBuf::create(0));
371 EXPECT_EQ(7, iobuf1->countChainElements());
372 EXPECT_EQ(14, iobuf1->computeChainDataLength());
373 // Check that nextBuf got set correctly to the buffer with 1 byte left
374 EXPECT_EQ(1, cursor.peekBytes().size());
375 cursor.read<uint8_t>();
379 // Check that inserting at the end doesn't create empty buf
380 RWPrivateCursor cursor(iobuf1.get());
381 Cursor(iobuf1.get()).clone(cloned, 1);
382 EXPECT_EQ(1, cloned->countChainElements());
383 EXPECT_EQ(1, cloned->computeChainDataLength());
387 cursor.insert(std::move(cloned));
388 EXPECT_EQ(8, iobuf1->countChainElements());
389 EXPECT_EQ(15, iobuf1->computeChainDataLength());
390 // Check that nextBuf got set correctly
391 cursor.read<uint8_t>();
394 // Check that inserting at the beginning doesn't create empty buf
395 RWPrivateCursor cursor(iobuf1.get());
396 Cursor(iobuf1.get()).clone(cloned, 1);
397 EXPECT_EQ(1, cloned->countChainElements());
398 EXPECT_EQ(1, cloned->computeChainDataLength());
400 cursor.insert(std::move(cloned));
401 EXPECT_EQ(9, iobuf1->countChainElements());
402 EXPECT_EQ(16, iobuf1->computeChainDataLength());
403 // Check that nextBuf got set correctly
404 cursor.read<uint8_t>();
408 TEST(IOBuf, cloneWithEmptyBufAtStart) {
409 folly::IOBufEqual eq;
410 auto empty = IOBuf::create(0);
411 auto hel = IOBuf::create(3);
413 auto lo = IOBuf::create(2);
416 auto iobuf = empty->clone();
417 iobuf->prependChain(hel->clone());
418 iobuf->prependChain(lo->clone());
419 iobuf->prependChain(empty->clone());
420 iobuf->prependChain(hel->clone());
421 iobuf->prependChain(lo->clone());
422 iobuf->prependChain(empty->clone());
423 iobuf->prependChain(lo->clone());
424 iobuf->prependChain(hel->clone());
425 iobuf->prependChain(lo->clone());
426 iobuf->prependChain(lo->clone());
428 Cursor cursor(iobuf.get());
429 std::unique_ptr<IOBuf> cloned;
431 cursor.pull(&data, 3);
432 cursor.clone(cloned, 2);
433 EXPECT_EQ(1, cloned->countChainElements());
434 EXPECT_EQ(2, cloned->length());
435 EXPECT_TRUE(eq(lo, cloned));
437 cursor.pull(&data, 3);
438 EXPECT_EQ("hel", std::string(data, sizeof(data)));
441 cursor.clone(cloned, 2);
442 EXPECT_TRUE(eq(lo, cloned));
444 std::string hello = cursor.readFixedString(5);
445 cursor.clone(cloned, 2);
446 EXPECT_TRUE(eq(lo, cloned));
449 TEST(IOBuf, Appender) {
450 std::unique_ptr<IOBuf> head(IOBuf::create(10));
451 append(head, "hello");
453 Appender app(head.get(), 10);
454 uint32_t cap = head->capacity();
455 uint32_t len1 = app.length();
456 EXPECT_EQ(cap - 5, len1);
457 app.ensure(len1); // won't grow
458 EXPECT_EQ(len1, app.length());
459 app.ensure(len1 + 1); // will grow
460 EXPECT_LE(len1 + 1, app.length());
462 append(app, " world");
463 EXPECT_EQ("hello world", toString(*head));
466 TEST(IOBuf, Printf) {
467 IOBuf head(IOBuf::CREATE, 24);
468 Appender app(&head, 32);
470 app.printf("%s", "test");
471 EXPECT_EQ(head.length(), 4);
472 EXPECT_EQ(0, memcmp(head.data(), "test\0", 5));
474 app.printf("%d%s %s%s %#x", 32, "this string is",
475 "longer than our original allocation size,",
476 "and will therefore require a new allocation", 0x12345678);
477 // The tailroom should start with a nul byte now.
478 EXPECT_GE(head.prev()->tailroom(), 1u);
479 EXPECT_EQ(0, *head.prev()->tail());
481 EXPECT_EQ("test32this string is longer than our original "
482 "allocation size,and will therefore require a "
483 "new allocation 0x12345678",
484 head.moveToFbString().toStdString());
487 TEST(IOBuf, Format) {
488 IOBuf head(IOBuf::CREATE, 24);
489 Appender app(&head, 32);
491 format("{}", "test")(app);
492 EXPECT_EQ(head.length(), 4);
493 EXPECT_EQ(0, memcmp(head.data(), "test", 4));
495 auto fmt = format("{}{} {}{} {:#x}",
496 32, "this string is",
497 "longer than our original allocation size,",
498 "and will therefore require a new allocation",
501 EXPECT_EQ("test32this string is longer than our original "
502 "allocation size,and will therefore require a "
503 "new allocation 0x12345678",
504 head.moveToFbString().toStdString());
507 TEST(IOBuf, QueueAppender) {
508 folly::IOBufQueue queue;
510 // Allocate 100 bytes at once, but don't grow past 1024
511 QueueAppender app(&queue, 100);
512 size_t n = 1024 / sizeof(uint32_t);
513 for (uint32_t i = 0; i < n; ++i) {
517 // There must be a goodMallocSize between 100 and 1024...
518 EXPECT_LT(1u, queue.front()->countChainElements());
519 const IOBuf* buf = queue.front();
521 EXPECT_LE(100u, buf->capacity());
523 } while (buf != queue.front());
525 Cursor cursor(queue.front());
526 for (uint32_t i = 0; i < n; ++i) {
527 EXPECT_EQ(i, cursor.readBE<uint32_t>());
530 EXPECT_THROW({cursor.readBE<uint32_t>();}, std::out_of_range);
533 TEST(IOBuf, CursorOperators) {
534 // Test operators on a single-item chain
536 std::unique_ptr<IOBuf> chain1(IOBuf::create(20));
539 Cursor curs1(chain1.get());
540 EXPECT_EQ(0, curs1 - chain1.get());
541 EXPECT_FALSE(curs1.isAtEnd());
543 EXPECT_EQ(3, curs1 - chain1.get());
544 EXPECT_FALSE(curs1.isAtEnd());
546 EXPECT_EQ(10, curs1 - chain1.get());
547 EXPECT_TRUE(curs1.isAtEnd());
549 Cursor curs2(chain1.get());
550 EXPECT_EQ(0, curs2 - chain1.get());
551 EXPECT_EQ(10, curs1 - curs2);
552 EXPECT_THROW(curs2 - curs1, std::out_of_range);
555 // Test cross-chain operations
557 std::unique_ptr<IOBuf> chain1(IOBuf::create(20));
559 std::unique_ptr<IOBuf> chain2 = chain1->clone();
561 Cursor curs1(chain1.get());
562 Cursor curs2(chain2.get());
563 EXPECT_THROW(curs1 - curs2, std::out_of_range);
564 EXPECT_THROW(curs1 - chain2.get(), std::out_of_range);
567 // Test operations on multi-item chains
569 std::unique_ptr<IOBuf> chain(IOBuf::create(20));
571 chain->appendChain(chain->clone());
572 EXPECT_EQ(20, chain->computeChainDataLength());
574 Cursor curs1(chain.get());
576 Cursor curs2(chain.get());
578 EXPECT_EQ(2, curs1 - curs2);
579 EXPECT_EQ(5, curs1 - chain.get());
580 EXPECT_THROW(curs2 - curs1, std::out_of_range);
583 EXPECT_EQ(9, curs1 - curs2);
584 EXPECT_EQ(12, curs1 - chain.get());
585 EXPECT_THROW(curs2 - curs1, std::out_of_range);
588 EXPECT_EQ(2, curs1 - curs2);
589 EXPECT_THROW(curs2 - curs1, std::out_of_range);
592 // Test isAtEnd() with empty buffers at the end of a chain
594 auto iobuf1 = IOBuf::create(20);
596 iobuf1->trimStart(5);
598 Cursor c(iobuf1.get());
599 EXPECT_FALSE(c.isAtEnd());
601 EXPECT_TRUE(c.isAtEnd());
603 iobuf1->prependChain(IOBuf::create(10));
604 iobuf1->prependChain(IOBuf::create(10));
605 EXPECT_TRUE(c.isAtEnd());
606 iobuf1->prev()->append(5);
607 EXPECT_FALSE(c.isAtEnd());
609 EXPECT_TRUE(c.isAtEnd());
612 // Test canAdvance with a chain of items
614 auto chain = IOBuf::create(10);
616 chain->appendChain(chain->clone());
617 EXPECT_EQ(2, chain->countChainElements());
618 EXPECT_EQ(20, chain->computeChainDataLength());
620 Cursor c(chain.get());
621 for (size_t i = 0; i <= 20; ++i) {
622 EXPECT_TRUE(c.canAdvance(i));
624 EXPECT_FALSE(c.canAdvance(21));
626 EXPECT_TRUE(c.canAdvance(10));
627 EXPECT_FALSE(c.canAdvance(11));
631 TEST(IOBuf, StringOperations) {
632 // Test a single buffer with two null-terminated strings and an extra uint8_t
635 std::unique_ptr<IOBuf> chain(IOBuf::create(16));
636 Appender app(chain.get(), 0);
637 app.push(reinterpret_cast<const uint8_t*>("hello\0world\0\x01"), 13);
639 Cursor curs(chain.get());
640 EXPECT_STREQ("hello", curs.readTerminatedString().c_str());
641 EXPECT_STREQ("world", curs.readTerminatedString().c_str());
642 EXPECT_EQ(1, curs.read<uint8_t>());
645 // Test multiple buffers where the first is empty and the string starts in
646 // the second buffer.
648 std::unique_ptr<IOBuf> chain(IOBuf::create(8));
649 chain->prependChain(IOBuf::create(12));
650 Appender app(chain.get(), 0);
651 app.push(reinterpret_cast<const uint8_t*>("hello world\0"), 12);
653 Cursor curs(chain.get());
654 EXPECT_STREQ("hello world", curs.readTerminatedString().c_str());
657 // Test multiple buffers with a single null-terminated string spanning them
659 std::unique_ptr<IOBuf> chain(IOBuf::create(8));
660 chain->prependChain(IOBuf::create(8));
662 chain->next()->append(4);
663 RWPrivateCursor rwc(chain.get());
664 rwc.push(reinterpret_cast<const uint8_t*>("hello world\0"), 12);
666 Cursor curs(chain.get());
667 EXPECT_STREQ("hello world", curs.readTerminatedString().c_str());
670 // Test a reading a null-terminated string that's longer than the maximum
673 std::unique_ptr<IOBuf> chain(IOBuf::create(16));
674 Appender app(chain.get(), 0);
675 app.push(reinterpret_cast<const uint8_t*>("hello world\0"), 12);
677 Cursor curs(chain.get());
678 EXPECT_THROW(curs.readTerminatedString('\0', 5), std::length_error);
681 // Test reading a null-terminated string from a chain with an empty buffer at
684 std::unique_ptr<IOBuf> buf(IOBuf::create(8));
685 Appender app(buf.get(), 0);
686 app.push(reinterpret_cast<const uint8_t*>("hello\0"), 6);
687 std::unique_ptr<IOBuf> chain(IOBuf::create(8));
688 chain->prependChain(std::move(buf));
690 Cursor curs(chain.get());
691 EXPECT_STREQ("hello", curs.readTerminatedString().c_str());
694 // Test reading a null-terminated string from a chain that doesn't contain the
697 std::unique_ptr<IOBuf> buf(IOBuf::create(8));
698 Appender app(buf.get(), 0);
699 app.push(reinterpret_cast<const uint8_t*>("hello"), 5);
700 std::unique_ptr<IOBuf> chain(IOBuf::create(8));
701 chain->prependChain(std::move(buf));
703 Cursor curs(chain.get());
704 EXPECT_THROW(curs.readTerminatedString(),
708 // Test reading a null-terminated string past the maximum length
710 std::unique_ptr<IOBuf> buf(IOBuf::create(8));
711 Appender app(buf.get(), 0);
712 app.push(reinterpret_cast<const uint8_t*>("hello\0"), 6);
713 std::unique_ptr<IOBuf> chain(IOBuf::create(8));
714 chain->prependChain(std::move(buf));
716 Cursor curs(chain.get());
717 EXPECT_THROW(curs.readTerminatedString('\0', 3),
721 // Test reading a two fixed-length strings from a single buffer with an extra
722 // uint8_t at the end
724 std::unique_ptr<IOBuf> chain(IOBuf::create(16));
725 Appender app(chain.get(), 0);
726 app.push(reinterpret_cast<const uint8_t*>("helloworld\x01"), 11);
728 Cursor curs(chain.get());
729 EXPECT_STREQ("hello", curs.readFixedString(5).c_str());
730 EXPECT_STREQ("world", curs.readFixedString(5).c_str());
731 EXPECT_EQ(1, curs.read<uint8_t>());
734 // Test multiple buffers where the first is empty and a fixed-length string
735 // starts in the second buffer.
737 std::unique_ptr<IOBuf> chain(IOBuf::create(8));
738 chain->prependChain(IOBuf::create(16));
739 Appender app(chain.get(), 0);
740 app.push(reinterpret_cast<const uint8_t*>("hello world"), 11);
742 Cursor curs(chain.get());
743 EXPECT_STREQ("hello world", curs.readFixedString(11).c_str());
746 // Test multiple buffers with a single fixed-length string spanning them
748 std::unique_ptr<IOBuf> chain(IOBuf::create(8));
749 chain->prependChain(IOBuf::create(8));
751 chain->next()->append(4);
752 RWPrivateCursor rwc(chain.get());
753 rwc.push(reinterpret_cast<const uint8_t*>("hello world"), 11);
755 Cursor curs(chain.get());
756 EXPECT_STREQ("hello world", curs.readFixedString(11).c_str());
759 // Test reading a fixed-length string from a chain with an empty buffer at
762 std::unique_ptr<IOBuf> buf(IOBuf::create(8));
763 Appender app(buf.get(), 0);
764 app.push(reinterpret_cast<const uint8_t*>("hello"), 5);
765 std::unique_ptr<IOBuf> chain(IOBuf::create(8));
766 chain->prependChain(std::move(buf));
768 Cursor curs(chain.get());
769 EXPECT_STREQ("hello", curs.readFixedString(5).c_str());
773 TEST(IOBuf, ReadWhileTrue) {
774 auto isAlpha = [](uint8_t ch) {
775 return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z');
777 auto isDigit = [](uint8_t ch) { return (ch >= '0' && ch <= '9'); };
779 // Test reading alternating alphabetic and numeric strings
781 std::unique_ptr<IOBuf> chain(IOBuf::create(32));
782 Appender app(chain.get(), 0);
783 app.push(StringPiece("hello123world456"));
785 Cursor curs(chain.get());
786 EXPECT_STREQ("hello", curs.readWhile(isAlpha).c_str());
787 EXPECT_STREQ("123", curs.readWhile(isDigit).c_str());
788 EXPECT_STREQ("world", curs.readWhile(isAlpha).c_str());
789 EXPECT_STREQ("456", curs.readWhile(isDigit).c_str());
790 EXPECT_TRUE(curs.isAtEnd());
793 // The same, but also use skipWhile()
795 std::unique_ptr<IOBuf> chain(IOBuf::create(16));
796 Appender app(chain.get(), 0);
797 app.push(StringPiece("hello123world456"));
799 Cursor curs(chain.get());
800 EXPECT_STREQ("hello", curs.readWhile(isAlpha).c_str());
801 curs.skipWhile(isDigit);
802 curs.skipWhile(isAlpha);
803 EXPECT_STREQ("456", curs.readWhile(isDigit).c_str());
804 EXPECT_TRUE(curs.isAtEnd());
807 // Test readWhile() using data split across multiple buffers,
808 // including some empty buffers in the middle of the chain.
810 std::unique_ptr<IOBuf> chain;
812 // First element in the chain has "he"
813 auto buf = IOBuf::create(40);
814 Appender app(buf.get(), 0);
815 app.push(StringPiece("he"));
816 chain = std::move(buf);
818 // The second element has "ll", after 10 bytes of headroom
819 buf = IOBuf::create(40);
821 app = Appender{buf.get(), 0};
822 app.push(StringPiece("ll"));
823 chain->prependChain(std::move(buf));
825 // The third element is empty
826 buf = IOBuf::create(40);
828 chain->prependChain(std::move(buf));
830 // The fourth element has "o12"
831 buf = IOBuf::create(40);
833 app = Appender{buf.get(), 0};
834 app.push(StringPiece("o12"));
835 chain->prependChain(std::move(buf));
837 // The fifth element has "3"
838 buf = IOBuf::create(40);
839 app = Appender{buf.get(), 0};
840 app.push(StringPiece("3"));
841 chain->prependChain(std::move(buf));
843 // The sixth element is empty
844 buf = IOBuf::create(40);
845 chain->prependChain(std::move(buf));
847 // The seventh element has "world456"
848 buf = IOBuf::create(40);
849 app = Appender{buf.get(), 0};
850 app.push(StringPiece("world456"));
851 chain->prependChain(std::move(buf));
853 // The eighth element is empty
854 buf = IOBuf::create(40);
855 chain->prependChain(std::move(buf));
857 Cursor curs(chain.get());
858 EXPECT_STREQ("hello", curs.readWhile(isAlpha).c_str());
859 EXPECT_STREQ("123", curs.readWhile(isDigit).c_str());
860 EXPECT_STREQ("world", curs.readWhile(isAlpha).c_str());
861 EXPECT_STREQ("456", curs.readWhile(isDigit).c_str());
862 EXPECT_TRUE(curs.isAtEnd());
866 TEST(IOBuf, TestAdvanceToEndSingle) {
867 std::unique_ptr<IOBuf> chain(IOBuf::create(10));
870 Cursor curs(chain.get());
872 EXPECT_TRUE(curs.isAtEnd());
873 EXPECT_EQ(curs - chain.get(), 10);
876 TEST(IOBuf, TestAdvanceToEndMulti) {
877 std::unique_ptr<IOBuf> chain(IOBuf::create(10));
880 std::unique_ptr<IOBuf> buf(IOBuf::create(5));
882 chain->prependChain(std::move(buf));
884 buf = IOBuf::create(20);
886 chain->prependChain(std::move(buf));
888 Cursor curs(chain.get());
890 EXPECT_TRUE(curs.isAtEnd());
891 EXPECT_EQ(curs - chain.get(), 35);
893 curs.reset(chain.get());
896 EXPECT_TRUE(curs.isAtEnd());
899 TEST(IOBuf, TestRetreatSingle) {
900 std::unique_ptr<IOBuf> chain(IOBuf::create(20));
903 Cursor curs(chain.get());
904 EXPECT_EQ(curs.retreatAtMost(0), 0);
905 EXPECT_EQ(curs.totalLength(), 20);
906 EXPECT_EQ(curs.retreatAtMost(5), 0);
907 EXPECT_EQ(curs.totalLength(), 20);
908 EXPECT_EQ(curs.retreatAtMost(25), 0);
909 EXPECT_EQ(curs.totalLength(), 20);
912 EXPECT_THROW(curs.retreat(5), std::out_of_range);
913 curs.reset(chain.get());
914 EXPECT_THROW(curs.retreat(25), std::out_of_range);
915 curs.reset(chain.get());
919 EXPECT_EQ(curs.totalLength(), 5);
921 EXPECT_EQ(curs.totalLength(), 15);
922 EXPECT_THROW(curs.retreat(10), std::out_of_range);
924 curs.reset(chain.get());
926 EXPECT_EQ(curs.retreatAtMost(5), 5);
927 EXPECT_EQ(curs.totalLength(), 5);
928 EXPECT_EQ(curs.retreatAtMost(10), 10);
929 EXPECT_EQ(curs.totalLength(), 15);
930 EXPECT_EQ(curs.retreatAtMost(10), 5);
931 EXPECT_EQ(curs.totalLength(), 20);
934 TEST(IOBuf, TestRetreatMulti) {
935 std::unique_ptr<IOBuf> chain(IOBuf::create(10));
938 std::unique_ptr<IOBuf> buf(IOBuf::create(5));
940 chain->prependChain(std::move(buf));
942 buf = IOBuf::create(20);
944 chain->prependChain(std::move(buf));
946 Cursor curs(chain.get());
947 EXPECT_EQ(curs.retreatAtMost(10), 0);
948 EXPECT_THROW(curs.retreat(10), std::out_of_range);
949 curs.reset(chain.get());
953 EXPECT_EQ(curs.totalLength(), 20);
954 EXPECT_EQ(curs.length(), 20);
956 EXPECT_EQ(curs.totalLength(), 21);
957 EXPECT_EQ(curs.length(), 1);
958 EXPECT_EQ(curs.retreatAtMost(50), 14);
959 EXPECT_EQ(curs.totalLength(), 35);
963 EXPECT_EQ(curs.totalLength(), 30);
966 TEST(IOBuf, TestRetreatOperators) {
967 std::unique_ptr<IOBuf> chain(IOBuf::create(20));
970 Cursor curs(chain.get());
973 EXPECT_EQ(curs.totalLength(), 5);
976 auto retreated = curs - 5;
977 EXPECT_EQ(retreated.totalLength(), 5);
978 EXPECT_EQ(curs.totalLength(), 0);