2 * Copyright 2015 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 <gflags/gflags.h>
20 #include <boost/random.hpp>
21 #include <gtest/gtest.h>
22 #include <folly/Benchmark.h>
23 #include <folly/Format.h>
24 #include <folly/Range.h>
25 #include <folly/io/Cursor.h>
26 #include <folly/io/Cursor-defs.h>
28 DECLARE_bool(benchmark);
30 using folly::ByteRange;
33 using folly::StringPiece;
34 using std::unique_ptr;
35 using namespace folly::io;
37 TEST(IOBuf, RWCursor) {
38 unique_ptr<IOBuf> iobuf1(IOBuf::create(20));
40 unique_ptr<IOBuf> iobuf2(IOBuf::create(20));
44 iobuf1->prependChain(std::move(iobuf2));
46 EXPECT_TRUE(iobuf1->isChained());
48 RWPrivateCursor wcursor(iobuf1.get());
49 Cursor rcursor(iobuf1.get());
50 wcursor.writeLE((uint64_t)1);
51 wcursor.writeLE((uint64_t)1);
52 wcursor.writeLE((uint64_t)1);
53 wcursor.write((uint8_t)1);
55 EXPECT_EQ(1, rcursor.readLE<uint64_t>());
57 EXPECT_EQ(1, rcursor.readLE<uint32_t>());
59 EXPECT_EQ(0, rcursor.read<uint8_t>());
60 EXPECT_EQ(0, rcursor.read<uint8_t>());
61 EXPECT_EQ(0, rcursor.read<uint8_t>());
62 EXPECT_EQ(0, rcursor.read<uint8_t>());
63 EXPECT_EQ(1, rcursor.read<uint8_t>());
67 unique_ptr<IOBuf> iobuf1(IOBuf::create(20));
69 RWPrivateCursor wcursor(iobuf1.get());
70 wcursor.write((uint8_t)1);
71 wcursor.write((uint8_t)2);
72 Cursor cursor(iobuf1.get());
74 EXPECT_EQ(2, cursor.read<uint8_t>());
78 unique_ptr<IOBuf> iobuf1(IOBuf::create(20));
80 RWPrivateCursor wcursor(iobuf1.get());
81 wcursor.write((uint8_t)1);
82 wcursor.write((uint8_t)2);
83 wcursor.reset(iobuf1.get());
84 EXPECT_EQ(1, wcursor.read<uint8_t>());
87 TEST(IOBuf, copy_assign_convert) {
88 unique_ptr<IOBuf> iobuf1(IOBuf::create(20));
90 RWPrivateCursor wcursor(iobuf1.get());
91 RWPrivateCursor cursor2(wcursor);
92 RWPrivateCursor cursor3(iobuf1.get());
94 wcursor.write((uint8_t)1);
96 wcursor.write((uint8_t)2);
97 Cursor cursor4(wcursor);
98 RWPrivateCursor cursor5(wcursor);
99 wcursor.write((uint8_t)3);
101 EXPECT_EQ(1, cursor2.read<uint8_t>());
102 EXPECT_EQ(2, cursor3.read<uint8_t>());
103 EXPECT_EQ(3, cursor4.read<uint8_t>());
106 TEST(IOBuf, arithmetic) {
107 IOBuf iobuf1(IOBuf::CREATE, 20);
109 RWPrivateCursor wcursor(&iobuf1);
111 wcursor.write((uint8_t)1);
112 Cursor cursor(&iobuf1);
114 EXPECT_EQ(1, cursor.read<uint8_t>());
116 Cursor start(&iobuf1);
117 Cursor cursor2 = start + 9;
118 EXPECT_EQ(7, cursor2 - cursor);
119 EXPECT_NE(cursor, cursor2);
121 cursor2 = cursor2 + 1;
122 EXPECT_EQ(cursor, cursor2);
125 TEST(IOBuf, endian) {
126 unique_ptr<IOBuf> iobuf1(IOBuf::create(20));
128 RWPrivateCursor wcursor(iobuf1.get());
129 Cursor rcursor(iobuf1.get());
134 // Try a couple combinations to ensure they were generated correctly
139 EXPECT_EQ(v, rcursor.readBE<uint16_t>());
142 TEST(IOBuf, Cursor) {
143 unique_ptr<IOBuf> iobuf1(IOBuf::create(1));
145 RWPrivateCursor c(iobuf1.get());
146 c.write((uint8_t)40); // OK
148 c.write((uint8_t)10); // Bad write, checked should except.
149 EXPECT_EQ(true, false);
154 TEST(IOBuf, UnshareCursor) {
156 unique_ptr<IOBuf> iobuf1(IOBuf::wrapBuffer(&buf, 1));
157 unique_ptr<IOBuf> iobuf2(IOBuf::wrapBuffer(&buf, 1));
158 RWUnshareCursor c1(iobuf1.get());
159 RWUnshareCursor c2(iobuf2.get());
161 c1.write((uint8_t)10); // This should duplicate the two buffers.
162 uint8_t t = c2.read<uint8_t>();
165 iobuf1 = IOBuf::wrapBuffer(&buf, 1);
166 iobuf2 = IOBuf::wrapBuffer(&buf, 1);
167 RWPrivateCursor c3(iobuf1.get());
168 RWPrivateCursor c4(iobuf2.get());
170 c3.write((uint8_t)10); // This should _not_ duplicate the two buffers.
171 t = c4.read<uint8_t>();
176 void append(std::unique_ptr<IOBuf>& buf, folly::StringPiece data) {
177 EXPECT_LE(data.size(), buf->tailroom());
178 memcpy(buf->writableData(), data.data(), data.size());
179 buf->append(data.size());
182 void append(Appender& appender, StringPiece data) {
183 appender.push(ByteRange(data));
186 std::string toString(const IOBuf& buf) {
189 std::pair<const uint8_t*, size_t> p;
190 while ((p = cursor.peek()).second) {
191 str.append(reinterpret_cast<const char*>(p.first), p.second);
192 cursor.skip(p.second);
199 TEST(IOBuf, PullAndPeek) {
200 std::unique_ptr<IOBuf> iobuf1(IOBuf::create(10));
201 append(iobuf1, "he");
202 std::unique_ptr<IOBuf> iobuf2(IOBuf::create(10));
203 append(iobuf2, "llo ");
204 std::unique_ptr<IOBuf> iobuf3(IOBuf::create(10));
205 append(iobuf3, "world");
206 iobuf1->prependChain(std::move(iobuf2));
207 iobuf1->prependChain(std::move(iobuf3));
208 EXPECT_EQ(3, iobuf1->countChainElements());
209 EXPECT_EQ(11, iobuf1->computeChainDataLength());
212 memset(buf, 0, sizeof(buf));
213 Cursor(iobuf1.get()).pull(buf, 11);
214 EXPECT_EQ("hello world", std::string(buf));
216 memset(buf, 0, sizeof(buf));
217 EXPECT_EQ(11, Cursor(iobuf1.get()).pullAtMost(buf, 20));
218 EXPECT_EQ("hello world", std::string(buf));
220 EXPECT_THROW({Cursor(iobuf1.get()).pull(buf, 20);},
224 RWPrivateCursor cursor(iobuf1.get());
225 auto p = cursor.peek();
226 EXPECT_EQ("he", std::string(reinterpret_cast<const char*>(p.first),
228 cursor.skip(p.second);
230 EXPECT_EQ("llo ", std::string(reinterpret_cast<const char*>(p.first),
232 cursor.skip(p.second);
234 EXPECT_EQ("world", std::string(reinterpret_cast<const char*>(p.first),
236 cursor.skip(p.second);
237 EXPECT_EQ(3, iobuf1->countChainElements());
238 EXPECT_EQ(11, iobuf1->computeChainDataLength());
242 RWPrivateCursor cursor(iobuf1.get());
244 auto p = cursor.peek();
245 EXPECT_EQ("hello world", std::string(reinterpret_cast<const
246 char*>(p.first), p.second));
247 EXPECT_EQ(1, iobuf1->countChainElements());
248 EXPECT_EQ(11, iobuf1->computeChainDataLength());
252 TEST(IOBuf, pushCursorData) {
253 unique_ptr<IOBuf> iobuf1(IOBuf::create(20));
255 iobuf1->trimStart(5);
256 unique_ptr<IOBuf> iobuf2(IOBuf::create(10));
257 unique_ptr<IOBuf> iobuf3(IOBuf::create(10));
260 iobuf1->prependChain(std::move(iobuf2));
261 iobuf1->prependChain(std::move(iobuf3));
262 EXPECT_TRUE(iobuf1->isChained());
264 //write 20 bytes to the buffer chain
265 RWPrivateCursor wcursor(iobuf1.get());
266 EXPECT_FALSE(wcursor.isAtEnd());
267 wcursor.writeBE<uint64_t>(1);
268 wcursor.writeBE<uint64_t>(10);
269 wcursor.writeBE<uint32_t>(20);
270 EXPECT_TRUE(wcursor.isAtEnd());
272 // create a read buffer for the buffer chain
273 Cursor rcursor(iobuf1.get());
274 EXPECT_EQ(1, rcursor.readBE<uint64_t>());
275 EXPECT_EQ(10, rcursor.readBE<uint64_t>());
276 EXPECT_EQ(20, rcursor.readBE<uint32_t>());
277 EXPECT_EQ(0, rcursor.totalLength());
278 rcursor.reset(iobuf1.get());
279 EXPECT_EQ(20, rcursor.totalLength());
281 // create another write buffer
282 unique_ptr<IOBuf> iobuf4(IOBuf::create(30));
284 RWPrivateCursor wcursor2(iobuf4.get());
285 // write buffer chain data into it, now wcursor2 should only
286 // have 10 bytes writable space
287 wcursor2.push(rcursor, 20);
288 EXPECT_EQ(wcursor2.totalLength(), 10);
289 // write again with not enough space in rcursor
290 EXPECT_THROW(wcursor2.push(rcursor, 20), std::out_of_range);
292 // create a read cursor to check iobuf3 data back
293 Cursor rcursor2(iobuf4.get());
294 EXPECT_EQ(1, rcursor2.readBE<uint64_t>());
295 EXPECT_EQ(10, rcursor2.readBE<uint64_t>());
296 EXPECT_EQ(20, rcursor2.readBE<uint32_t>());
300 TEST(IOBuf, Gather) {
301 std::unique_ptr<IOBuf> iobuf1(IOBuf::create(10));
302 append(iobuf1, "he");
303 std::unique_ptr<IOBuf> iobuf2(IOBuf::create(10));
304 append(iobuf2, "llo ");
305 std::unique_ptr<IOBuf> iobuf3(IOBuf::create(10));
306 append(iobuf3, "world");
307 iobuf1->prependChain(std::move(iobuf2));
308 iobuf1->prependChain(std::move(iobuf3));
309 EXPECT_EQ(3, iobuf1->countChainElements());
310 EXPECT_EQ(11, iobuf1->computeChainDataLength());
312 // Attempting to gather() more data than available in the chain should fail.
313 // Try from the very beginning of the chain.
314 RWPrivateCursor cursor(iobuf1.get());
315 EXPECT_THROW(cursor.gather(15), std::overflow_error);
316 // Now try from the middle of the chain
318 EXPECT_THROW(cursor.gather(10), std::overflow_error);
320 // Calling gatherAtMost() should succeed, however, and just gather
322 cursor.gatherAtMost(10);
323 EXPECT_EQ(8, cursor.length());
324 EXPECT_EQ(8, cursor.totalLength());
325 EXPECT_FALSE(cursor.isAtEnd());
326 EXPECT_EQ("lo world",
327 folly::StringPiece(reinterpret_cast<const char*>(cursor.data()),
329 EXPECT_EQ(2, iobuf1->countChainElements());
330 EXPECT_EQ(11, iobuf1->computeChainDataLength());
332 // Now try gather again on the chain head
333 cursor = RWPrivateCursor(iobuf1.get());
335 // Since gather() doesn't split buffers, everything should be collapsed into
336 // a single buffer now.
337 EXPECT_EQ(1, iobuf1->countChainElements());
338 EXPECT_EQ(11, iobuf1->computeChainDataLength());
339 EXPECT_EQ(11, cursor.length());
340 EXPECT_EQ(11, cursor.totalLength());
343 TEST(IOBuf, cloneAndInsert) {
344 std::unique_ptr<IOBuf> iobuf1(IOBuf::create(10));
345 append(iobuf1, "he");
346 std::unique_ptr<IOBuf> iobuf2(IOBuf::create(10));
347 append(iobuf2, "llo ");
348 std::unique_ptr<IOBuf> iobuf3(IOBuf::create(10));
349 append(iobuf3, "world");
350 iobuf1->prependChain(std::move(iobuf2));
351 iobuf1->prependChain(std::move(iobuf3));
352 EXPECT_EQ(3, iobuf1->countChainElements());
353 EXPECT_EQ(11, iobuf1->computeChainDataLength());
355 std::unique_ptr<IOBuf> cloned;
357 Cursor(iobuf1.get()).clone(cloned, 3);
358 EXPECT_EQ(2, cloned->countChainElements());
359 EXPECT_EQ(3, cloned->computeChainDataLength());
362 EXPECT_EQ(11, Cursor(iobuf1.get()).cloneAtMost(cloned, 20));
363 EXPECT_EQ(3, cloned->countChainElements());
364 EXPECT_EQ(11, cloned->computeChainDataLength());
367 EXPECT_THROW({Cursor(iobuf1.get()).clone(cloned, 20);},
371 // Check that inserting in the middle of an iobuf splits
372 RWPrivateCursor cursor(iobuf1.get());
373 Cursor(iobuf1.get()).clone(cloned, 3);
374 EXPECT_EQ(2, cloned->countChainElements());
375 EXPECT_EQ(3, cloned->computeChainDataLength());
379 cursor.insert(std::move(cloned));
380 cursor.insert(folly::IOBuf::create(0));
381 EXPECT_EQ(7, iobuf1->countChainElements());
382 EXPECT_EQ(14, iobuf1->computeChainDataLength());
383 // Check that nextBuf got set correctly to the buffer with 1 byte left
384 EXPECT_EQ(1, cursor.peek().second);
385 cursor.read<uint8_t>();
389 // Check that inserting at the end doesn't create empty buf
390 RWPrivateCursor cursor(iobuf1.get());
391 Cursor(iobuf1.get()).clone(cloned, 1);
392 EXPECT_EQ(1, cloned->countChainElements());
393 EXPECT_EQ(1, cloned->computeChainDataLength());
397 cursor.insert(std::move(cloned));
398 EXPECT_EQ(8, iobuf1->countChainElements());
399 EXPECT_EQ(15, iobuf1->computeChainDataLength());
400 // Check that nextBuf got set correctly
401 cursor.read<uint8_t>();
404 // Check that inserting at the beginning doesn't create empty buf
405 RWPrivateCursor cursor(iobuf1.get());
406 Cursor(iobuf1.get()).clone(cloned, 1);
407 EXPECT_EQ(1, cloned->countChainElements());
408 EXPECT_EQ(1, cloned->computeChainDataLength());
410 cursor.insert(std::move(cloned));
411 EXPECT_EQ(9, iobuf1->countChainElements());
412 EXPECT_EQ(16, iobuf1->computeChainDataLength());
413 // Check that nextBuf got set correctly
414 cursor.read<uint8_t>();
418 TEST(IOBuf, cloneWithEmptyBufAtStart) {
419 folly::IOBufEqual eq;
420 auto empty = IOBuf::create(0);
421 auto hel = IOBuf::create(3);
423 auto lo = IOBuf::create(2);
426 auto iobuf = empty->clone();
427 iobuf->prependChain(hel->clone());
428 iobuf->prependChain(lo->clone());
429 iobuf->prependChain(empty->clone());
430 iobuf->prependChain(hel->clone());
431 iobuf->prependChain(lo->clone());
432 iobuf->prependChain(empty->clone());
433 iobuf->prependChain(lo->clone());
434 iobuf->prependChain(hel->clone());
435 iobuf->prependChain(lo->clone());
436 iobuf->prependChain(lo->clone());
438 Cursor cursor(iobuf.get());
439 std::unique_ptr<IOBuf> cloned;
441 cursor.pull(&data, 3);
442 cursor.clone(cloned, 2);
443 EXPECT_EQ(1, cloned->countChainElements());
444 EXPECT_EQ(2, cloned->length());
445 EXPECT_TRUE(eq(lo, cloned));
447 cursor.pull(&data, 3);
448 EXPECT_EQ("hel", std::string(data, sizeof(data)));
451 cursor.clone(cloned, 2);
452 EXPECT_TRUE(eq(lo, cloned));
454 std::string hello = cursor.readFixedString(5);
455 cursor.clone(cloned, 2);
456 EXPECT_TRUE(eq(lo, cloned));
459 TEST(IOBuf, Appender) {
460 std::unique_ptr<IOBuf> head(IOBuf::create(10));
461 append(head, "hello");
463 Appender app(head.get(), 10);
464 uint32_t cap = head->capacity();
465 uint32_t len1 = app.length();
466 EXPECT_EQ(cap - 5, len1);
467 app.ensure(len1); // won't grow
468 EXPECT_EQ(len1, app.length());
469 app.ensure(len1 + 1); // will grow
470 EXPECT_LE(len1 + 1, app.length());
472 append(app, " world");
473 EXPECT_EQ("hello world", toString(*head));
476 TEST(IOBuf, Printf) {
477 IOBuf head(IOBuf::CREATE, 24);
478 Appender app(&head, 32);
480 app.printf("%s", "test");
481 EXPECT_EQ(head.length(), 4);
482 EXPECT_EQ(0, memcmp(head.data(), "test\0", 5));
484 app.printf("%d%s %s%s %#x", 32, "this string is",
485 "longer than our original allocation size,",
486 "and will therefore require a new allocation", 0x12345678);
487 // The tailroom should start with a nul byte now.
488 EXPECT_GE(head.prev()->tailroom(), 1);
489 EXPECT_EQ(0, *head.prev()->tail());
491 EXPECT_EQ("test32this string is longer than our original "
492 "allocation size,and will therefore require a "
493 "new allocation 0x12345678",
494 head.moveToFbString().toStdString());
497 TEST(IOBuf, Format) {
498 IOBuf head(IOBuf::CREATE, 24);
499 Appender app(&head, 32);
501 format("{}", "test")(app);
502 EXPECT_EQ(head.length(), 4);
503 EXPECT_EQ(0, memcmp(head.data(), "test", 4));
505 auto fmt = format("{}{} {}{} {:#x}",
506 32, "this string is",
507 "longer than our original allocation size,",
508 "and will therefore require a new allocation",
511 EXPECT_EQ("test32this string is longer than our original "
512 "allocation size,and will therefore require a "
513 "new allocation 0x12345678",
514 head.moveToFbString().toStdString());
517 TEST(IOBuf, QueueAppender) {
518 folly::IOBufQueue queue;
520 // Allocate 100 bytes at once, but don't grow past 1024
521 QueueAppender app(&queue, 100);
522 size_t n = 1024 / sizeof(uint32_t);
523 for (uint32_t i = 0; i < n; ++i) {
527 // There must be a goodMallocSize between 100 and 1024...
528 EXPECT_LT(1, queue.front()->countChainElements());
529 const IOBuf* buf = queue.front();
531 EXPECT_LE(100, buf->capacity());
533 } while (buf != queue.front());
535 Cursor cursor(queue.front());
536 for (uint32_t i = 0; i < n; ++i) {
537 EXPECT_EQ(i, cursor.readBE<uint32_t>());
540 EXPECT_THROW({cursor.readBE<uint32_t>();}, std::out_of_range);
543 TEST(IOBuf, CursorOperators) {
544 // Test operators on a single-item chain
546 std::unique_ptr<IOBuf> chain1(IOBuf::create(20));
549 Cursor curs1(chain1.get());
550 EXPECT_EQ(0, curs1 - chain1.get());
551 EXPECT_FALSE(curs1.isAtEnd());
553 EXPECT_EQ(3, curs1 - chain1.get());
554 EXPECT_FALSE(curs1.isAtEnd());
556 EXPECT_EQ(10, curs1 - chain1.get());
557 EXPECT_TRUE(curs1.isAtEnd());
559 Cursor curs2(chain1.get());
560 EXPECT_EQ(0, curs2 - chain1.get());
561 EXPECT_EQ(10, curs1 - curs2);
562 EXPECT_THROW(curs2 - curs1, std::out_of_range);
565 // Test cross-chain operations
567 std::unique_ptr<IOBuf> chain1(IOBuf::create(20));
569 std::unique_ptr<IOBuf> chain2 = chain1->clone();
571 Cursor curs1(chain1.get());
572 Cursor curs2(chain2.get());
573 EXPECT_THROW(curs1 - curs2, std::out_of_range);
574 EXPECT_THROW(curs1 - chain2.get(), std::out_of_range);
577 // Test operations on multi-item chains
579 std::unique_ptr<IOBuf> chain(IOBuf::create(20));
581 chain->appendChain(chain->clone());
582 EXPECT_EQ(20, chain->computeChainDataLength());
584 Cursor curs1(chain.get());
586 Cursor curs2(chain.get());
588 EXPECT_EQ(2, curs1 - curs2);
589 EXPECT_EQ(5, curs1 - chain.get());
590 EXPECT_THROW(curs2 - curs1, std::out_of_range);
593 EXPECT_EQ(9, curs1 - curs2);
594 EXPECT_EQ(12, curs1 - chain.get());
595 EXPECT_THROW(curs2 - curs1, std::out_of_range);
598 EXPECT_EQ(2, curs1 - curs2);
599 EXPECT_THROW(curs2 - curs1, std::out_of_range);
602 // Test isAtEnd() with empty buffers at the end of a chain
604 auto iobuf1 = IOBuf::create(20);
606 iobuf1->trimStart(5);
608 Cursor c(iobuf1.get());
609 EXPECT_FALSE(c.isAtEnd());
611 EXPECT_TRUE(c.isAtEnd());
613 iobuf1->prependChain(IOBuf::create(10));
614 iobuf1->prependChain(IOBuf::create(10));
615 EXPECT_TRUE(c.isAtEnd());
616 iobuf1->prev()->append(5);
617 EXPECT_FALSE(c.isAtEnd());
619 EXPECT_TRUE(c.isAtEnd());
623 TEST(IOBuf, StringOperations) {
624 // Test a single buffer with two null-terminated strings and an extra uint8_t
627 std::unique_ptr<IOBuf> chain(IOBuf::create(16));
628 Appender app(chain.get(), 0);
629 app.push(reinterpret_cast<const uint8_t*>("hello\0world\0\x01"), 13);
631 Cursor curs(chain.get());
632 EXPECT_STREQ("hello", curs.readTerminatedString().c_str());
633 EXPECT_STREQ("world", curs.readTerminatedString().c_str());
634 EXPECT_EQ(1, curs.read<uint8_t>());
637 // Test multiple buffers where the first is empty and the string starts in
638 // the second buffer.
640 std::unique_ptr<IOBuf> chain(IOBuf::create(8));
641 chain->prependChain(IOBuf::create(12));
642 Appender app(chain.get(), 0);
643 app.push(reinterpret_cast<const uint8_t*>("hello world\0"), 12);
645 Cursor curs(chain.get());
646 EXPECT_STREQ("hello world", curs.readTerminatedString().c_str());
649 // Test multiple buffers with a single null-terminated string spanning them
651 std::unique_ptr<IOBuf> chain(IOBuf::create(8));
652 chain->prependChain(IOBuf::create(8));
654 chain->next()->append(4);
655 RWPrivateCursor rwc(chain.get());
656 rwc.push(reinterpret_cast<const uint8_t*>("hello world\0"), 12);
658 Cursor curs(chain.get());
659 EXPECT_STREQ("hello world", curs.readTerminatedString().c_str());
662 // Test a reading a null-terminated string that's longer than the maximum
665 std::unique_ptr<IOBuf> chain(IOBuf::create(16));
666 Appender app(chain.get(), 0);
667 app.push(reinterpret_cast<const uint8_t*>("hello world\0"), 12);
669 Cursor curs(chain.get());
670 EXPECT_THROW(curs.readTerminatedString('\0', 5), std::length_error);
673 // Test reading a null-terminated string from a chain with an empty buffer at
676 std::unique_ptr<IOBuf> buf(IOBuf::create(8));
677 Appender app(buf.get(), 0);
678 app.push(reinterpret_cast<const uint8_t*>("hello\0"), 6);
679 std::unique_ptr<IOBuf> chain(IOBuf::create(8));
680 chain->prependChain(std::move(buf));
682 Cursor curs(chain.get());
683 EXPECT_STREQ("hello", curs.readTerminatedString().c_str());
686 // Test reading a two fixed-length strings from a single buffer with an extra
687 // uint8_t at the end
689 std::unique_ptr<IOBuf> chain(IOBuf::create(16));
690 Appender app(chain.get(), 0);
691 app.push(reinterpret_cast<const uint8_t*>("helloworld\x01"), 11);
693 Cursor curs(chain.get());
694 EXPECT_STREQ("hello", curs.readFixedString(5).c_str());
695 EXPECT_STREQ("world", curs.readFixedString(5).c_str());
696 EXPECT_EQ(1, curs.read<uint8_t>());
699 // Test multiple buffers where the first is empty and a fixed-length string
700 // starts in the second buffer.
702 std::unique_ptr<IOBuf> chain(IOBuf::create(8));
703 chain->prependChain(IOBuf::create(16));
704 Appender app(chain.get(), 0);
705 app.push(reinterpret_cast<const uint8_t*>("hello world"), 11);
707 Cursor curs(chain.get());
708 EXPECT_STREQ("hello world", curs.readFixedString(11).c_str());
711 // Test multiple buffers with a single fixed-length string spanning them
713 std::unique_ptr<IOBuf> chain(IOBuf::create(8));
714 chain->prependChain(IOBuf::create(8));
716 chain->next()->append(4);
717 RWPrivateCursor rwc(chain.get());
718 rwc.push(reinterpret_cast<const uint8_t*>("hello world"), 11);
720 Cursor curs(chain.get());
721 EXPECT_STREQ("hello world", curs.readFixedString(11).c_str());
724 // Test reading a fixed-length string from a chain with an empty buffer at
727 std::unique_ptr<IOBuf> buf(IOBuf::create(8));
728 Appender app(buf.get(), 0);
729 app.push(reinterpret_cast<const uint8_t*>("hello"), 5);
730 std::unique_ptr<IOBuf> chain(IOBuf::create(8));
731 chain->prependChain(std::move(buf));
733 Cursor curs(chain.get());
734 EXPECT_STREQ("hello", curs.readFixedString(5).c_str());
738 int benchmark_size = 1000;
739 unique_ptr<IOBuf> iobuf_benchmark;
741 unique_ptr<IOBuf> iobuf_read_benchmark;
743 template <class CursClass>
744 void runBenchmark() {
745 CursClass c(iobuf_benchmark.get());
747 for(int i = 0; i < benchmark_size; i++) {
752 BENCHMARK(rwPrivateCursorBenchmark, iters) {
754 runBenchmark<RWPrivateCursor>();
758 BENCHMARK(rwUnshareCursorBenchmark, iters) {
760 runBenchmark<RWUnshareCursor>();
765 BENCHMARK(cursorBenchmark, iters) {
767 Cursor c(iobuf_read_benchmark.get());
768 for(int i = 0; i < benchmark_size ; i++) {
774 BENCHMARK(skipBenchmark, iters) {
776 Cursor c(iobuf_read_benchmark.get());
777 for(int i = 0; i < benchmark_size ; i++) {
785 // _bin/folly/experimental/io/test/iobuf_cursor_test -benchmark
787 // Benchmark Iters Total t t/iter iter/sec
788 // ---------------------------------------------------------------------------
789 // rwPrivateCursorBenchmark 100000 142.9 ms 1.429 us 683.5 k
790 // rwUnshareCursorBenchmark 100000 309.3 ms 3.093 us 315.7 k
791 // cursorBenchmark 100000 741.4 ms 7.414 us 131.7 k
792 // skipBenchmark 100000 738.9 ms 7.389 us 132.2 k
796 // Linux dev2159.snc6.facebook.com 2.6.33-7_fbk15_104e4d0 #1 SMP
797 // Tue Oct 19 22:40:30 PDT 2010 x86_64 x86_64 x86_64 GNU/Linux
799 // 72GB RAM, 2 CPUs (Intel(R) Xeon(R) CPU L5630 @ 2.13GHz)
800 // hyperthreading disabled
802 int main(int argc, char** argv) {
803 testing::InitGoogleTest(&argc, argv);
804 gflags::ParseCommandLineFlags(&argc, &argv, true);
806 auto ret = RUN_ALL_TESTS();
808 if (ret == 0 && FLAGS_benchmark) {
809 iobuf_benchmark = IOBuf::create(benchmark_size);
810 iobuf_benchmark->append(benchmark_size);
812 iobuf_read_benchmark = IOBuf::create(1);
813 for (int i = 0; i < benchmark_size; i++) {
814 unique_ptr<IOBuf> iobuf2(IOBuf::create(1));
816 iobuf_read_benchmark->prependChain(std::move(iobuf2));
819 folly::runBenchmarks();