2 * Copyright 2016 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>
23 #include <gtest/gtest.h>
25 using folly::ByteRange;
28 using folly::StringPiece;
29 using std::unique_ptr;
30 using namespace folly::io;
32 TEST(IOBuf, RWCursor) {
33 unique_ptr<IOBuf> iobuf1(IOBuf::create(20));
35 unique_ptr<IOBuf> iobuf2(IOBuf::create(20));
39 iobuf1->prependChain(std::move(iobuf2));
41 EXPECT_TRUE(iobuf1->isChained());
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);
50 EXPECT_EQ(1, rcursor.readLE<uint64_t>());
52 EXPECT_EQ(1, rcursor.readLE<uint32_t>());
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>());
62 unique_ptr<IOBuf> iobuf1(IOBuf::create(20));
64 RWPrivateCursor wcursor(iobuf1.get());
65 wcursor.write((uint8_t)1);
66 wcursor.write((uint8_t)2);
67 Cursor cursor(iobuf1.get());
69 EXPECT_EQ(2, cursor.read<uint8_t>());
73 unique_ptr<IOBuf> iobuf1(IOBuf::create(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>());
82 TEST(IOBuf, copy_assign_convert) {
83 unique_ptr<IOBuf> iobuf1(IOBuf::create(20));
85 RWPrivateCursor wcursor(iobuf1.get());
86 RWPrivateCursor cursor2(wcursor);
87 RWPrivateCursor cursor3(iobuf1.get());
89 wcursor.write((uint8_t)1);
91 wcursor.write((uint8_t)2);
92 Cursor cursor4(wcursor);
93 RWPrivateCursor cursor5(wcursor);
94 wcursor.write((uint8_t)3);
96 EXPECT_EQ(1, cursor2.read<uint8_t>());
97 EXPECT_EQ(2, cursor3.read<uint8_t>());
98 EXPECT_EQ(3, cursor4.read<uint8_t>());
101 TEST(IOBuf, arithmetic) {
102 IOBuf iobuf1(IOBuf::CREATE, 20);
104 RWPrivateCursor wcursor(&iobuf1);
106 wcursor.write((uint8_t)1);
107 Cursor cursor(&iobuf1);
109 EXPECT_EQ(1, cursor.read<uint8_t>());
111 Cursor start(&iobuf1);
112 Cursor cursor2 = start + 9;
113 EXPECT_EQ(7, cursor2 - cursor);
114 EXPECT_NE(cursor, cursor2);
116 cursor2 = cursor2 + 1;
117 EXPECT_EQ(cursor, cursor2);
120 TEST(IOBuf, endian) {
121 unique_ptr<IOBuf> iobuf1(IOBuf::create(20));
123 RWPrivateCursor wcursor(iobuf1.get());
124 Cursor rcursor(iobuf1.get());
129 // Try a couple combinations to ensure they were generated correctly
134 EXPECT_EQ(v, rcursor.readBE<uint16_t>());
137 TEST(IOBuf, Cursor) {
138 unique_ptr<IOBuf> iobuf1(IOBuf::create(1));
140 RWPrivateCursor c(iobuf1.get());
141 c.write((uint8_t)40); // OK
143 c.write((uint8_t)10); // Bad write, checked should except.
144 EXPECT_EQ(true, false);
149 TEST(IOBuf, UnshareCursor) {
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());
156 c1.write((uint8_t)10); // This should duplicate the two buffers.
157 uint8_t t = c2.read<uint8_t>();
160 iobuf1 = IOBuf::wrapBuffer(&buf, 1);
161 iobuf2 = IOBuf::wrapBuffer(&buf, 1);
162 RWPrivateCursor c3(iobuf1.get());
163 RWPrivateCursor c4(iobuf2.get());
165 c3.write((uint8_t)10); // This should _not_ duplicate the two buffers.
166 t = c4.read<uint8_t>();
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());
177 void append(Appender& appender, StringPiece data) {
178 appender.push(ByteRange(data));
181 std::string toString(const IOBuf& buf) {
185 while (!(b = cursor.peekBytes()).empty()) {
186 str.append(reinterpret_cast<const char*>(b.data()), b.size());
187 cursor.skip(b.size());
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());
207 memset(buf, 0, sizeof(buf));
208 Cursor(iobuf1.get()).pull(buf, 11);
209 EXPECT_EQ("hello world", std::string(buf));
211 memset(buf, 0, sizeof(buf));
212 EXPECT_EQ(11, Cursor(iobuf1.get()).pullAtMost(buf, 20));
213 EXPECT_EQ("hello world", std::string(buf));
215 EXPECT_THROW({Cursor(iobuf1.get()).pull(buf, 20);},
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());
234 RWPrivateCursor cursor(iobuf1.get());
236 auto b = cursor.peekBytes();
237 EXPECT_EQ("hello world", StringPiece(b));
238 EXPECT_EQ(1, iobuf1->countChainElements());
239 EXPECT_EQ(11, iobuf1->computeChainDataLength());
243 TEST(IOBuf, pushCursorData) {
244 unique_ptr<IOBuf> iobuf1(IOBuf::create(20));
246 iobuf1->trimStart(5);
247 unique_ptr<IOBuf> iobuf2(IOBuf::create(10));
248 unique_ptr<IOBuf> iobuf3(IOBuf::create(10));
251 iobuf1->prependChain(std::move(iobuf2));
252 iobuf1->prependChain(std::move(iobuf3));
253 EXPECT_TRUE(iobuf1->isChained());
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());
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());
272 // create another write buffer
273 unique_ptr<IOBuf> iobuf4(IOBuf::create(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);
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>());
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());
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
309 EXPECT_THROW(cursor.gather(10), std::overflow_error);
311 // Calling gatherAtMost() should succeed, however, and just gather
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()),
320 EXPECT_EQ(2, iobuf1->countChainElements());
321 EXPECT_EQ(11, iobuf1->computeChainDataLength());
323 // Now try gather again on the chain head
324 cursor = RWPrivateCursor(iobuf1.get());
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());
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());
346 std::unique_ptr<IOBuf> cloned;
348 Cursor(iobuf1.get()).clone(cloned, 3);
349 EXPECT_EQ(2, cloned->countChainElements());
350 EXPECT_EQ(3, cloned->computeChainDataLength());
353 EXPECT_EQ(11, Cursor(iobuf1.get()).cloneAtMost(cloned, 20));
354 EXPECT_EQ(3, cloned->countChainElements());
355 EXPECT_EQ(11, cloned->computeChainDataLength());
358 EXPECT_THROW({Cursor(iobuf1.get()).clone(cloned, 20);},
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());
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>();
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());
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>();
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());
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>();
409 TEST(IOBuf, cloneWithEmptyBufAtStart) {
410 folly::IOBufEqual eq;
411 auto empty = IOBuf::create(0);
412 auto hel = IOBuf::create(3);
414 auto lo = IOBuf::create(2);
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());
429 Cursor cursor(iobuf.get());
430 std::unique_ptr<IOBuf> cloned;
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));
438 cursor.pull(&data, 3);
439 EXPECT_EQ("hel", std::string(data, sizeof(data)));
442 cursor.clone(cloned, 2);
443 EXPECT_TRUE(eq(lo, cloned));
445 std::string hello = cursor.readFixedString(5);
446 cursor.clone(cloned, 2);
447 EXPECT_TRUE(eq(lo, cloned));
450 TEST(IOBuf, Appender) {
451 std::unique_ptr<IOBuf> head(IOBuf::create(10));
452 append(head, "hello");
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());
463 append(app, " world");
464 EXPECT_EQ("hello world", toString(*head));
467 TEST(IOBuf, Printf) {
468 IOBuf head(IOBuf::CREATE, 24);
469 Appender app(&head, 32);
471 app.printf("%s", "test");
472 EXPECT_EQ(head.length(), 4);
473 EXPECT_EQ(0, memcmp(head.data(), "test\0", 5));
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());
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());
488 TEST(IOBuf, Format) {
489 IOBuf head(IOBuf::CREATE, 24);
490 Appender app(&head, 32);
492 format("{}", "test")(app);
493 EXPECT_EQ(head.length(), 4);
494 EXPECT_EQ(0, memcmp(head.data(), "test", 4));
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",
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());
508 TEST(IOBuf, QueueAppender) {
509 folly::IOBufQueue queue;
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) {
518 // There must be a goodMallocSize between 100 and 1024...
519 EXPECT_LT(1, queue.front()->countChainElements());
520 const IOBuf* buf = queue.front();
522 EXPECT_LE(100, buf->capacity());
524 } while (buf != queue.front());
526 Cursor cursor(queue.front());
527 for (uint32_t i = 0; i < n; ++i) {
528 EXPECT_EQ(i, cursor.readBE<uint32_t>());
531 EXPECT_THROW({cursor.readBE<uint32_t>();}, std::out_of_range);
534 TEST(IOBuf, CursorOperators) {
535 // Test operators on a single-item chain
537 std::unique_ptr<IOBuf> chain1(IOBuf::create(20));
540 Cursor curs1(chain1.get());
541 EXPECT_EQ(0, curs1 - chain1.get());
542 EXPECT_FALSE(curs1.isAtEnd());
544 EXPECT_EQ(3, curs1 - chain1.get());
545 EXPECT_FALSE(curs1.isAtEnd());
547 EXPECT_EQ(10, curs1 - chain1.get());
548 EXPECT_TRUE(curs1.isAtEnd());
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);
556 // Test cross-chain operations
558 std::unique_ptr<IOBuf> chain1(IOBuf::create(20));
560 std::unique_ptr<IOBuf> chain2 = chain1->clone();
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);
568 // Test operations on multi-item chains
570 std::unique_ptr<IOBuf> chain(IOBuf::create(20));
572 chain->appendChain(chain->clone());
573 EXPECT_EQ(20, chain->computeChainDataLength());
575 Cursor curs1(chain.get());
577 Cursor curs2(chain.get());
579 EXPECT_EQ(2, curs1 - curs2);
580 EXPECT_EQ(5, curs1 - chain.get());
581 EXPECT_THROW(curs2 - curs1, std::out_of_range);
584 EXPECT_EQ(9, curs1 - curs2);
585 EXPECT_EQ(12, curs1 - chain.get());
586 EXPECT_THROW(curs2 - curs1, std::out_of_range);
589 EXPECT_EQ(2, curs1 - curs2);
590 EXPECT_THROW(curs2 - curs1, std::out_of_range);
593 // Test isAtEnd() with empty buffers at the end of a chain
595 auto iobuf1 = IOBuf::create(20);
597 iobuf1->trimStart(5);
599 Cursor c(iobuf1.get());
600 EXPECT_FALSE(c.isAtEnd());
602 EXPECT_TRUE(c.isAtEnd());
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());
610 EXPECT_TRUE(c.isAtEnd());
613 // Test canAdvance with a chain of items
615 auto chain = IOBuf::create(10);
617 chain->appendChain(chain->clone());
618 EXPECT_EQ(2, chain->countChainElements());
619 EXPECT_EQ(20, chain->computeChainDataLength());
621 Cursor c(chain.get());
622 for (size_t i = 0; i <= 20; ++i) {
623 EXPECT_TRUE(c.canAdvance(i));
625 EXPECT_FALSE(c.canAdvance(21));
627 EXPECT_TRUE(c.canAdvance(10));
628 EXPECT_FALSE(c.canAdvance(11));
632 TEST(IOBuf, StringOperations) {
633 // Test a single buffer with two null-terminated strings and an extra uint8_t
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);
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>());
646 // Test multiple buffers where the first is empty and the string starts in
647 // the second buffer.
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);
654 Cursor curs(chain.get());
655 EXPECT_STREQ("hello world", curs.readTerminatedString().c_str());
658 // Test multiple buffers with a single null-terminated string spanning them
660 std::unique_ptr<IOBuf> chain(IOBuf::create(8));
661 chain->prependChain(IOBuf::create(8));
663 chain->next()->append(4);
664 RWPrivateCursor rwc(chain.get());
665 rwc.push(reinterpret_cast<const uint8_t*>("hello world\0"), 12);
667 Cursor curs(chain.get());
668 EXPECT_STREQ("hello world", curs.readTerminatedString().c_str());
671 // Test a reading a null-terminated string that's longer than the maximum
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);
678 Cursor curs(chain.get());
679 EXPECT_THROW(curs.readTerminatedString('\0', 5), std::length_error);
682 // Test reading a null-terminated string from a chain with an empty buffer at
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));
691 Cursor curs(chain.get());
692 EXPECT_STREQ("hello", curs.readTerminatedString().c_str());
695 // Test reading a null-terminated string from a chain that doesn't contain the
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));
704 Cursor curs(chain.get());
705 EXPECT_THROW(curs.readTerminatedString(),
709 // Test reading a null-terminated string past the maximum length
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));
717 Cursor curs(chain.get());
718 EXPECT_THROW(curs.readTerminatedString('\0', 3),
722 // Test reading a two fixed-length strings from a single buffer with an extra
723 // uint8_t at the end
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);
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>());
735 // Test multiple buffers where the first is empty and a fixed-length string
736 // starts in the second buffer.
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);
743 Cursor curs(chain.get());
744 EXPECT_STREQ("hello world", curs.readFixedString(11).c_str());
747 // Test multiple buffers with a single fixed-length string spanning them
749 std::unique_ptr<IOBuf> chain(IOBuf::create(8));
750 chain->prependChain(IOBuf::create(8));
752 chain->next()->append(4);
753 RWPrivateCursor rwc(chain.get());
754 rwc.push(reinterpret_cast<const uint8_t*>("hello world"), 11);
756 Cursor curs(chain.get());
757 EXPECT_STREQ("hello world", curs.readFixedString(11).c_str());
760 // Test reading a fixed-length string from a chain with an empty buffer at
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));
769 Cursor curs(chain.get());
770 EXPECT_STREQ("hello", curs.readFixedString(5).c_str());
774 TEST(IOBuf, ReadWhileTrue) {
775 auto isAlpha = [](uint8_t ch) {
776 return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z');
778 auto isDigit = [](uint8_t ch) { return (ch >= '0' && ch <= '9'); };
780 // Test reading alternating alphabetic and numeric strings
782 std::unique_ptr<IOBuf> chain(IOBuf::create(32));
783 Appender app(chain.get(), 0);
784 app.push(StringPiece("hello123world456"));
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());
794 // The same, but also use skipWhile()
796 std::unique_ptr<IOBuf> chain(IOBuf::create(16));
797 Appender app(chain.get(), 0);
798 app.push(StringPiece("hello123world456"));
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());
808 // Test readWhile() using data split across multiple buffers,
809 // including some empty buffers in the middle of the chain.
811 std::unique_ptr<IOBuf> chain;
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);
819 // The second element has "ll", after 10 bytes of headroom
820 buf = IOBuf::create(40);
822 app = Appender{buf.get(), 0};
823 app.push(StringPiece("ll"));
824 chain->prependChain(std::move(buf));
826 // The third element is empty
827 buf = IOBuf::create(40);
829 chain->prependChain(std::move(buf));
831 // The fourth element has "o12"
832 buf = IOBuf::create(40);
834 app = Appender{buf.get(), 0};
835 app.push(StringPiece("o12"));
836 chain->prependChain(std::move(buf));
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));
844 // The sixth element is empty
845 buf = IOBuf::create(40);
846 chain->prependChain(std::move(buf));
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));
854 // The eighth element is empty
855 buf = IOBuf::create(40);
856 chain->prependChain(std::move(buf));
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());