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>
22 #include <folly/io/Cursor-defs.h>
24 #include <gtest/gtest.h>
26 using folly::ByteRange;
29 using folly::StringPiece;
30 using std::unique_ptr;
31 using namespace folly::io;
33 TEST(IOBuf, RWCursor) {
34 unique_ptr<IOBuf> iobuf1(IOBuf::create(20));
36 unique_ptr<IOBuf> iobuf2(IOBuf::create(20));
40 iobuf1->prependChain(std::move(iobuf2));
42 EXPECT_TRUE(iobuf1->isChained());
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);
51 EXPECT_EQ(1, rcursor.readLE<uint64_t>());
53 EXPECT_EQ(1, rcursor.readLE<uint32_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(0, rcursor.read<uint8_t>());
59 EXPECT_EQ(1, rcursor.read<uint8_t>());
63 unique_ptr<IOBuf> iobuf1(IOBuf::create(20));
65 RWPrivateCursor wcursor(iobuf1.get());
66 wcursor.write((uint8_t)1);
67 wcursor.write((uint8_t)2);
68 Cursor cursor(iobuf1.get());
70 EXPECT_EQ(2, cursor.read<uint8_t>());
74 unique_ptr<IOBuf> iobuf1(IOBuf::create(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>());
83 TEST(IOBuf, copy_assign_convert) {
84 unique_ptr<IOBuf> iobuf1(IOBuf::create(20));
86 RWPrivateCursor wcursor(iobuf1.get());
87 RWPrivateCursor cursor2(wcursor);
88 RWPrivateCursor cursor3(iobuf1.get());
90 wcursor.write((uint8_t)1);
92 wcursor.write((uint8_t)2);
93 Cursor cursor4(wcursor);
94 RWPrivateCursor cursor5(wcursor);
95 wcursor.write((uint8_t)3);
97 EXPECT_EQ(1, cursor2.read<uint8_t>());
98 EXPECT_EQ(2, cursor3.read<uint8_t>());
99 EXPECT_EQ(3, cursor4.read<uint8_t>());
102 TEST(IOBuf, arithmetic) {
103 IOBuf iobuf1(IOBuf::CREATE, 20);
105 RWPrivateCursor wcursor(&iobuf1);
107 wcursor.write((uint8_t)1);
108 Cursor cursor(&iobuf1);
110 EXPECT_EQ(1, cursor.read<uint8_t>());
112 Cursor start(&iobuf1);
113 Cursor cursor2 = start + 9;
114 EXPECT_EQ(7, cursor2 - cursor);
115 EXPECT_NE(cursor, cursor2);
117 cursor2 = cursor2 + 1;
118 EXPECT_EQ(cursor, cursor2);
121 TEST(IOBuf, endian) {
122 unique_ptr<IOBuf> iobuf1(IOBuf::create(20));
124 RWPrivateCursor wcursor(iobuf1.get());
125 Cursor rcursor(iobuf1.get());
130 // Try a couple combinations to ensure they were generated correctly
135 EXPECT_EQ(v, rcursor.readBE<uint16_t>());
138 TEST(IOBuf, Cursor) {
139 unique_ptr<IOBuf> iobuf1(IOBuf::create(1));
141 RWPrivateCursor c(iobuf1.get());
142 c.write((uint8_t)40); // OK
144 c.write((uint8_t)10); // Bad write, checked should except.
145 EXPECT_EQ(true, false);
150 TEST(IOBuf, UnshareCursor) {
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());
157 c1.write((uint8_t)10); // This should duplicate the two buffers.
158 uint8_t t = c2.read<uint8_t>();
161 iobuf1 = IOBuf::wrapBuffer(&buf, 1);
162 iobuf2 = IOBuf::wrapBuffer(&buf, 1);
163 RWPrivateCursor c3(iobuf1.get());
164 RWPrivateCursor c4(iobuf2.get());
166 c3.write((uint8_t)10); // This should _not_ duplicate the two buffers.
167 t = c4.read<uint8_t>();
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());
178 void append(Appender& appender, StringPiece data) {
179 appender.push(ByteRange(data));
182 std::string toString(const IOBuf& buf) {
185 std::pair<const uint8_t*, size_t> p;
186 while ((p = cursor.peek()).second) {
187 str.append(reinterpret_cast<const char*>(p.first), p.second);
188 cursor.skip(p.second);
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());
208 memset(buf, 0, sizeof(buf));
209 Cursor(iobuf1.get()).pull(buf, 11);
210 EXPECT_EQ("hello world", std::string(buf));
212 memset(buf, 0, sizeof(buf));
213 EXPECT_EQ(11, Cursor(iobuf1.get()).pullAtMost(buf, 20));
214 EXPECT_EQ("hello world", std::string(buf));
216 EXPECT_THROW({Cursor(iobuf1.get()).pull(buf, 20);},
220 RWPrivateCursor cursor(iobuf1.get());
221 auto p = cursor.peek();
222 EXPECT_EQ("he", std::string(reinterpret_cast<const char*>(p.first),
224 cursor.skip(p.second);
226 EXPECT_EQ("llo ", std::string(reinterpret_cast<const char*>(p.first),
228 cursor.skip(p.second);
230 EXPECT_EQ("world", std::string(reinterpret_cast<const char*>(p.first),
232 cursor.skip(p.second);
233 EXPECT_EQ(3, iobuf1->countChainElements());
234 EXPECT_EQ(11, iobuf1->computeChainDataLength());
238 RWPrivateCursor cursor(iobuf1.get());
240 auto p = cursor.peek();
241 EXPECT_EQ("hello world", std::string(reinterpret_cast<const
242 char*>(p.first), p.second));
243 EXPECT_EQ(1, iobuf1->countChainElements());
244 EXPECT_EQ(11, iobuf1->computeChainDataLength());
248 TEST(IOBuf, pushCursorData) {
249 unique_ptr<IOBuf> iobuf1(IOBuf::create(20));
251 iobuf1->trimStart(5);
252 unique_ptr<IOBuf> iobuf2(IOBuf::create(10));
253 unique_ptr<IOBuf> iobuf3(IOBuf::create(10));
256 iobuf1->prependChain(std::move(iobuf2));
257 iobuf1->prependChain(std::move(iobuf3));
258 EXPECT_TRUE(iobuf1->isChained());
260 //write 20 bytes to the buffer chain
261 RWPrivateCursor wcursor(iobuf1.get());
262 EXPECT_FALSE(wcursor.isAtEnd());
263 wcursor.writeBE<uint64_t>(1);
264 wcursor.writeBE<uint64_t>(10);
265 wcursor.writeBE<uint32_t>(20);
266 EXPECT_TRUE(wcursor.isAtEnd());
268 // create a read buffer for the buffer chain
269 Cursor rcursor(iobuf1.get());
270 EXPECT_EQ(1, rcursor.readBE<uint64_t>());
271 EXPECT_EQ(10, rcursor.readBE<uint64_t>());
272 EXPECT_EQ(20, rcursor.readBE<uint32_t>());
273 EXPECT_EQ(0, rcursor.totalLength());
274 rcursor.reset(iobuf1.get());
275 EXPECT_EQ(20, rcursor.totalLength());
277 // create another write buffer
278 unique_ptr<IOBuf> iobuf4(IOBuf::create(30));
280 RWPrivateCursor wcursor2(iobuf4.get());
281 // write buffer chain data into it, now wcursor2 should only
282 // have 10 bytes writable space
283 wcursor2.push(rcursor, 20);
284 EXPECT_EQ(wcursor2.totalLength(), 10);
285 // write again with not enough space in rcursor
286 EXPECT_THROW(wcursor2.push(rcursor, 20), std::out_of_range);
288 // create a read cursor to check iobuf3 data back
289 Cursor rcursor2(iobuf4.get());
290 EXPECT_EQ(1, rcursor2.readBE<uint64_t>());
291 EXPECT_EQ(10, rcursor2.readBE<uint64_t>());
292 EXPECT_EQ(20, rcursor2.readBE<uint32_t>());
296 TEST(IOBuf, Gather) {
297 std::unique_ptr<IOBuf> iobuf1(IOBuf::create(10));
298 append(iobuf1, "he");
299 std::unique_ptr<IOBuf> iobuf2(IOBuf::create(10));
300 append(iobuf2, "llo ");
301 std::unique_ptr<IOBuf> iobuf3(IOBuf::create(10));
302 append(iobuf3, "world");
303 iobuf1->prependChain(std::move(iobuf2));
304 iobuf1->prependChain(std::move(iobuf3));
305 EXPECT_EQ(3, iobuf1->countChainElements());
306 EXPECT_EQ(11, iobuf1->computeChainDataLength());
308 // Attempting to gather() more data than available in the chain should fail.
309 // Try from the very beginning of the chain.
310 RWPrivateCursor cursor(iobuf1.get());
311 EXPECT_THROW(cursor.gather(15), std::overflow_error);
312 // Now try from the middle of the chain
314 EXPECT_THROW(cursor.gather(10), std::overflow_error);
316 // Calling gatherAtMost() should succeed, however, and just gather
318 cursor.gatherAtMost(10);
319 EXPECT_EQ(8, cursor.length());
320 EXPECT_EQ(8, cursor.totalLength());
321 EXPECT_FALSE(cursor.isAtEnd());
322 EXPECT_EQ("lo world",
323 folly::StringPiece(reinterpret_cast<const char*>(cursor.data()),
325 EXPECT_EQ(2, iobuf1->countChainElements());
326 EXPECT_EQ(11, iobuf1->computeChainDataLength());
328 // Now try gather again on the chain head
329 cursor = RWPrivateCursor(iobuf1.get());
331 // Since gather() doesn't split buffers, everything should be collapsed into
332 // a single buffer now.
333 EXPECT_EQ(1, iobuf1->countChainElements());
334 EXPECT_EQ(11, iobuf1->computeChainDataLength());
335 EXPECT_EQ(11, cursor.length());
336 EXPECT_EQ(11, cursor.totalLength());
339 TEST(IOBuf, cloneAndInsert) {
340 std::unique_ptr<IOBuf> iobuf1(IOBuf::create(10));
341 append(iobuf1, "he");
342 std::unique_ptr<IOBuf> iobuf2(IOBuf::create(10));
343 append(iobuf2, "llo ");
344 std::unique_ptr<IOBuf> iobuf3(IOBuf::create(10));
345 append(iobuf3, "world");
346 iobuf1->prependChain(std::move(iobuf2));
347 iobuf1->prependChain(std::move(iobuf3));
348 EXPECT_EQ(3, iobuf1->countChainElements());
349 EXPECT_EQ(11, iobuf1->computeChainDataLength());
351 std::unique_ptr<IOBuf> cloned;
353 Cursor(iobuf1.get()).clone(cloned, 3);
354 EXPECT_EQ(2, cloned->countChainElements());
355 EXPECT_EQ(3, cloned->computeChainDataLength());
358 EXPECT_EQ(11, Cursor(iobuf1.get()).cloneAtMost(cloned, 20));
359 EXPECT_EQ(3, cloned->countChainElements());
360 EXPECT_EQ(11, cloned->computeChainDataLength());
363 EXPECT_THROW({Cursor(iobuf1.get()).clone(cloned, 20);},
367 // Check that inserting in the middle of an iobuf splits
368 RWPrivateCursor cursor(iobuf1.get());
369 Cursor(iobuf1.get()).clone(cloned, 3);
370 EXPECT_EQ(2, cloned->countChainElements());
371 EXPECT_EQ(3, cloned->computeChainDataLength());
375 cursor.insert(std::move(cloned));
376 cursor.insert(folly::IOBuf::create(0));
377 EXPECT_EQ(7, iobuf1->countChainElements());
378 EXPECT_EQ(14, iobuf1->computeChainDataLength());
379 // Check that nextBuf got set correctly to the buffer with 1 byte left
380 EXPECT_EQ(1, cursor.peek().second);
381 cursor.read<uint8_t>();
385 // Check that inserting at the end doesn't create empty buf
386 RWPrivateCursor cursor(iobuf1.get());
387 Cursor(iobuf1.get()).clone(cloned, 1);
388 EXPECT_EQ(1, cloned->countChainElements());
389 EXPECT_EQ(1, cloned->computeChainDataLength());
393 cursor.insert(std::move(cloned));
394 EXPECT_EQ(8, iobuf1->countChainElements());
395 EXPECT_EQ(15, iobuf1->computeChainDataLength());
396 // Check that nextBuf got set correctly
397 cursor.read<uint8_t>();
400 // Check that inserting at the beginning doesn't create empty buf
401 RWPrivateCursor cursor(iobuf1.get());
402 Cursor(iobuf1.get()).clone(cloned, 1);
403 EXPECT_EQ(1, cloned->countChainElements());
404 EXPECT_EQ(1, cloned->computeChainDataLength());
406 cursor.insert(std::move(cloned));
407 EXPECT_EQ(9, iobuf1->countChainElements());
408 EXPECT_EQ(16, iobuf1->computeChainDataLength());
409 // Check that nextBuf got set correctly
410 cursor.read<uint8_t>();
414 TEST(IOBuf, cloneWithEmptyBufAtStart) {
415 folly::IOBufEqual eq;
416 auto empty = IOBuf::create(0);
417 auto hel = IOBuf::create(3);
419 auto lo = IOBuf::create(2);
422 auto iobuf = empty->clone();
423 iobuf->prependChain(hel->clone());
424 iobuf->prependChain(lo->clone());
425 iobuf->prependChain(empty->clone());
426 iobuf->prependChain(hel->clone());
427 iobuf->prependChain(lo->clone());
428 iobuf->prependChain(empty->clone());
429 iobuf->prependChain(lo->clone());
430 iobuf->prependChain(hel->clone());
431 iobuf->prependChain(lo->clone());
432 iobuf->prependChain(lo->clone());
434 Cursor cursor(iobuf.get());
435 std::unique_ptr<IOBuf> cloned;
437 cursor.pull(&data, 3);
438 cursor.clone(cloned, 2);
439 EXPECT_EQ(1, cloned->countChainElements());
440 EXPECT_EQ(2, cloned->length());
441 EXPECT_TRUE(eq(lo, cloned));
443 cursor.pull(&data, 3);
444 EXPECT_EQ("hel", std::string(data, sizeof(data)));
447 cursor.clone(cloned, 2);
448 EXPECT_TRUE(eq(lo, cloned));
450 std::string hello = cursor.readFixedString(5);
451 cursor.clone(cloned, 2);
452 EXPECT_TRUE(eq(lo, cloned));
455 TEST(IOBuf, Appender) {
456 std::unique_ptr<IOBuf> head(IOBuf::create(10));
457 append(head, "hello");
459 Appender app(head.get(), 10);
460 uint32_t cap = head->capacity();
461 uint32_t len1 = app.length();
462 EXPECT_EQ(cap - 5, len1);
463 app.ensure(len1); // won't grow
464 EXPECT_EQ(len1, app.length());
465 app.ensure(len1 + 1); // will grow
466 EXPECT_LE(len1 + 1, app.length());
468 append(app, " world");
469 EXPECT_EQ("hello world", toString(*head));
472 TEST(IOBuf, Printf) {
473 IOBuf head(IOBuf::CREATE, 24);
474 Appender app(&head, 32);
476 app.printf("%s", "test");
477 EXPECT_EQ(head.length(), 4);
478 EXPECT_EQ(0, memcmp(head.data(), "test\0", 5));
480 app.printf("%d%s %s%s %#x", 32, "this string is",
481 "longer than our original allocation size,",
482 "and will therefore require a new allocation", 0x12345678);
483 // The tailroom should start with a nul byte now.
484 EXPECT_GE(head.prev()->tailroom(), 1);
485 EXPECT_EQ(0, *head.prev()->tail());
487 EXPECT_EQ("test32this string is longer than our original "
488 "allocation size,and will therefore require a "
489 "new allocation 0x12345678",
490 head.moveToFbString().toStdString());
493 TEST(IOBuf, Format) {
494 IOBuf head(IOBuf::CREATE, 24);
495 Appender app(&head, 32);
497 format("{}", "test")(app);
498 EXPECT_EQ(head.length(), 4);
499 EXPECT_EQ(0, memcmp(head.data(), "test", 4));
501 auto fmt = format("{}{} {}{} {:#x}",
502 32, "this string is",
503 "longer than our original allocation size,",
504 "and will therefore require a new allocation",
507 EXPECT_EQ("test32this string is longer than our original "
508 "allocation size,and will therefore require a "
509 "new allocation 0x12345678",
510 head.moveToFbString().toStdString());
513 TEST(IOBuf, QueueAppender) {
514 folly::IOBufQueue queue;
516 // Allocate 100 bytes at once, but don't grow past 1024
517 QueueAppender app(&queue, 100);
518 size_t n = 1024 / sizeof(uint32_t);
519 for (uint32_t i = 0; i < n; ++i) {
523 // There must be a goodMallocSize between 100 and 1024...
524 EXPECT_LT(1, queue.front()->countChainElements());
525 const IOBuf* buf = queue.front();
527 EXPECT_LE(100, buf->capacity());
529 } while (buf != queue.front());
531 Cursor cursor(queue.front());
532 for (uint32_t i = 0; i < n; ++i) {
533 EXPECT_EQ(i, cursor.readBE<uint32_t>());
536 EXPECT_THROW({cursor.readBE<uint32_t>();}, std::out_of_range);
539 TEST(IOBuf, CursorOperators) {
540 // Test operators on a single-item chain
542 std::unique_ptr<IOBuf> chain1(IOBuf::create(20));
545 Cursor curs1(chain1.get());
546 EXPECT_EQ(0, curs1 - chain1.get());
547 EXPECT_FALSE(curs1.isAtEnd());
549 EXPECT_EQ(3, curs1 - chain1.get());
550 EXPECT_FALSE(curs1.isAtEnd());
552 EXPECT_EQ(10, curs1 - chain1.get());
553 EXPECT_TRUE(curs1.isAtEnd());
555 Cursor curs2(chain1.get());
556 EXPECT_EQ(0, curs2 - chain1.get());
557 EXPECT_EQ(10, curs1 - curs2);
558 EXPECT_THROW(curs2 - curs1, std::out_of_range);
561 // Test cross-chain operations
563 std::unique_ptr<IOBuf> chain1(IOBuf::create(20));
565 std::unique_ptr<IOBuf> chain2 = chain1->clone();
567 Cursor curs1(chain1.get());
568 Cursor curs2(chain2.get());
569 EXPECT_THROW(curs1 - curs2, std::out_of_range);
570 EXPECT_THROW(curs1 - chain2.get(), std::out_of_range);
573 // Test operations on multi-item chains
575 std::unique_ptr<IOBuf> chain(IOBuf::create(20));
577 chain->appendChain(chain->clone());
578 EXPECT_EQ(20, chain->computeChainDataLength());
580 Cursor curs1(chain.get());
582 Cursor curs2(chain.get());
584 EXPECT_EQ(2, curs1 - curs2);
585 EXPECT_EQ(5, curs1 - chain.get());
586 EXPECT_THROW(curs2 - curs1, std::out_of_range);
589 EXPECT_EQ(9, curs1 - curs2);
590 EXPECT_EQ(12, curs1 - chain.get());
591 EXPECT_THROW(curs2 - curs1, std::out_of_range);
594 EXPECT_EQ(2, curs1 - curs2);
595 EXPECT_THROW(curs2 - curs1, std::out_of_range);
598 // Test isAtEnd() with empty buffers at the end of a chain
600 auto iobuf1 = IOBuf::create(20);
602 iobuf1->trimStart(5);
604 Cursor c(iobuf1.get());
605 EXPECT_FALSE(c.isAtEnd());
607 EXPECT_TRUE(c.isAtEnd());
609 iobuf1->prependChain(IOBuf::create(10));
610 iobuf1->prependChain(IOBuf::create(10));
611 EXPECT_TRUE(c.isAtEnd());
612 iobuf1->prev()->append(5);
613 EXPECT_FALSE(c.isAtEnd());
615 EXPECT_TRUE(c.isAtEnd());
618 // Test canAdvance with a chain of items
620 auto chain = IOBuf::create(10);
622 chain->appendChain(chain->clone());
623 EXPECT_EQ(2, chain->countChainElements());
624 EXPECT_EQ(20, chain->computeChainDataLength());
626 Cursor c(chain.get());
627 for (size_t i = 0; i <= 20; ++i) {
628 EXPECT_TRUE(c.canAdvance(i));
630 EXPECT_FALSE(c.canAdvance(21));
632 EXPECT_TRUE(c.canAdvance(10));
633 EXPECT_FALSE(c.canAdvance(11));
637 TEST(IOBuf, StringOperations) {
638 // Test a single buffer with two null-terminated strings and an extra uint8_t
641 std::unique_ptr<IOBuf> chain(IOBuf::create(16));
642 Appender app(chain.get(), 0);
643 app.push(reinterpret_cast<const uint8_t*>("hello\0world\0\x01"), 13);
645 Cursor curs(chain.get());
646 EXPECT_STREQ("hello", curs.readTerminatedString().c_str());
647 EXPECT_STREQ("world", curs.readTerminatedString().c_str());
648 EXPECT_EQ(1, curs.read<uint8_t>());
651 // Test multiple buffers where the first is empty and the string starts in
652 // the second buffer.
654 std::unique_ptr<IOBuf> chain(IOBuf::create(8));
655 chain->prependChain(IOBuf::create(12));
656 Appender app(chain.get(), 0);
657 app.push(reinterpret_cast<const uint8_t*>("hello world\0"), 12);
659 Cursor curs(chain.get());
660 EXPECT_STREQ("hello world", curs.readTerminatedString().c_str());
663 // Test multiple buffers with a single null-terminated string spanning them
665 std::unique_ptr<IOBuf> chain(IOBuf::create(8));
666 chain->prependChain(IOBuf::create(8));
668 chain->next()->append(4);
669 RWPrivateCursor rwc(chain.get());
670 rwc.push(reinterpret_cast<const uint8_t*>("hello world\0"), 12);
672 Cursor curs(chain.get());
673 EXPECT_STREQ("hello world", curs.readTerminatedString().c_str());
676 // Test a reading a null-terminated string that's longer than the maximum
679 std::unique_ptr<IOBuf> chain(IOBuf::create(16));
680 Appender app(chain.get(), 0);
681 app.push(reinterpret_cast<const uint8_t*>("hello world\0"), 12);
683 Cursor curs(chain.get());
684 EXPECT_THROW(curs.readTerminatedString('\0', 5), std::length_error);
687 // Test reading a null-terminated string from a chain with an empty buffer at
690 std::unique_ptr<IOBuf> buf(IOBuf::create(8));
691 Appender app(buf.get(), 0);
692 app.push(reinterpret_cast<const uint8_t*>("hello\0"), 6);
693 std::unique_ptr<IOBuf> chain(IOBuf::create(8));
694 chain->prependChain(std::move(buf));
696 Cursor curs(chain.get());
697 EXPECT_STREQ("hello", curs.readTerminatedString().c_str());
700 // Test reading a null-terminated string from a chain that doesn't contain the
703 std::unique_ptr<IOBuf> buf(IOBuf::create(8));
704 Appender app(buf.get(), 0);
705 app.push(reinterpret_cast<const uint8_t*>("hello"), 5);
706 std::unique_ptr<IOBuf> chain(IOBuf::create(8));
707 chain->prependChain(std::move(buf));
709 Cursor curs(chain.get());
710 EXPECT_THROW(curs.readTerminatedString(),
714 // Test reading a null-terminated string past the maximum length
716 std::unique_ptr<IOBuf> buf(IOBuf::create(8));
717 Appender app(buf.get(), 0);
718 app.push(reinterpret_cast<const uint8_t*>("hello\0"), 6);
719 std::unique_ptr<IOBuf> chain(IOBuf::create(8));
720 chain->prependChain(std::move(buf));
722 Cursor curs(chain.get());
723 EXPECT_THROW(curs.readTerminatedString('\0', 3),
727 // Test reading a two fixed-length strings from a single buffer with an extra
728 // uint8_t at the end
730 std::unique_ptr<IOBuf> chain(IOBuf::create(16));
731 Appender app(chain.get(), 0);
732 app.push(reinterpret_cast<const uint8_t*>("helloworld\x01"), 11);
734 Cursor curs(chain.get());
735 EXPECT_STREQ("hello", curs.readFixedString(5).c_str());
736 EXPECT_STREQ("world", curs.readFixedString(5).c_str());
737 EXPECT_EQ(1, curs.read<uint8_t>());
740 // Test multiple buffers where the first is empty and a fixed-length string
741 // starts in the second buffer.
743 std::unique_ptr<IOBuf> chain(IOBuf::create(8));
744 chain->prependChain(IOBuf::create(16));
745 Appender app(chain.get(), 0);
746 app.push(reinterpret_cast<const uint8_t*>("hello world"), 11);
748 Cursor curs(chain.get());
749 EXPECT_STREQ("hello world", curs.readFixedString(11).c_str());
752 // Test multiple buffers with a single fixed-length string spanning them
754 std::unique_ptr<IOBuf> chain(IOBuf::create(8));
755 chain->prependChain(IOBuf::create(8));
757 chain->next()->append(4);
758 RWPrivateCursor rwc(chain.get());
759 rwc.push(reinterpret_cast<const uint8_t*>("hello world"), 11);
761 Cursor curs(chain.get());
762 EXPECT_STREQ("hello world", curs.readFixedString(11).c_str());
765 // Test reading a fixed-length string from a chain with an empty buffer at
768 std::unique_ptr<IOBuf> buf(IOBuf::create(8));
769 Appender app(buf.get(), 0);
770 app.push(reinterpret_cast<const uint8_t*>("hello"), 5);
771 std::unique_ptr<IOBuf> chain(IOBuf::create(8));
772 chain->prependChain(std::move(buf));
774 Cursor curs(chain.get());
775 EXPECT_STREQ("hello", curs.readFixedString(5).c_str());