2 * Copyright 2014 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/Range.h"
24 #include "folly/io/Cursor.h"
26 DECLARE_bool(benchmark);
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));
38 IOBuf* iob2ptr = iobuf2.get();
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, overloading) {
102 unique_ptr<IOBuf> iobuf1(IOBuf::create(20));
104 RWPrivateCursor wcursor(iobuf1.get());
106 wcursor.write((uint8_t)1);
107 Cursor cursor(iobuf1.get());
109 EXPECT_EQ(1, cursor.read<uint8_t>());
112 TEST(IOBuf, endian) {
113 unique_ptr<IOBuf> iobuf1(IOBuf::create(20));
115 RWPrivateCursor wcursor(iobuf1.get());
116 Cursor rcursor(iobuf1.get());
121 // Try a couple combinations to ensure they were generated correctly
126 EXPECT_EQ(v, rcursor.readBE<uint16_t>());
129 TEST(IOBuf, Cursor) {
130 unique_ptr<IOBuf> iobuf1(IOBuf::create(1));
132 RWPrivateCursor c(iobuf1.get());
133 c.write((uint8_t)40); // OK
135 c.write((uint8_t)10); // Bad write, checked should except.
136 EXPECT_EQ(true, false);
141 TEST(IOBuf, UnshareCursor) {
143 unique_ptr<IOBuf> iobuf1(IOBuf::wrapBuffer(&buf, 1));
144 unique_ptr<IOBuf> iobuf2(IOBuf::wrapBuffer(&buf, 1));
145 RWUnshareCursor c1(iobuf1.get());
146 RWUnshareCursor c2(iobuf2.get());
148 c1.write((uint8_t)10); // This should duplicate the two buffers.
149 uint8_t t = c2.read<uint8_t>();
152 iobuf1 = IOBuf::wrapBuffer(&buf, 1);
153 iobuf2 = IOBuf::wrapBuffer(&buf, 1);
154 RWPrivateCursor c3(iobuf1.get());
155 RWPrivateCursor c4(iobuf2.get());
157 c3.write((uint8_t)10); // This should _not_ duplicate the two buffers.
158 t = c4.read<uint8_t>();
163 void append(std::unique_ptr<IOBuf>& buf, folly::StringPiece data) {
164 EXPECT_LE(data.size(), buf->tailroom());
165 memcpy(buf->writableData(), data.data(), data.size());
166 buf->append(data.size());
169 void append(Appender& appender, folly::StringPiece data) {
170 appender.push(reinterpret_cast<const uint8_t*>(data.data()), data.size());
173 std::string toString(const IOBuf& buf) {
176 std::pair<const uint8_t*, size_t> p;
177 while ((p = cursor.peek()).second) {
178 str.append(reinterpret_cast<const char*>(p.first), p.second);
179 cursor.skip(p.second);
186 TEST(IOBuf, PullAndPeek) {
187 std::unique_ptr<IOBuf> iobuf1(IOBuf::create(10));
188 append(iobuf1, "he");
189 std::unique_ptr<IOBuf> iobuf2(IOBuf::create(10));
190 append(iobuf2, "llo ");
191 std::unique_ptr<IOBuf> iobuf3(IOBuf::create(10));
192 append(iobuf3, "world");
193 iobuf1->prependChain(std::move(iobuf2));
194 iobuf1->prependChain(std::move(iobuf3));
195 EXPECT_EQ(3, iobuf1->countChainElements());
196 EXPECT_EQ(11, iobuf1->computeChainDataLength());
199 memset(buf, 0, sizeof(buf));
200 Cursor(iobuf1.get()).pull(buf, 11);
201 EXPECT_EQ("hello world", std::string(buf));
203 memset(buf, 0, sizeof(buf));
204 EXPECT_EQ(11, Cursor(iobuf1.get()).pullAtMost(buf, 20));
205 EXPECT_EQ("hello world", std::string(buf));
207 EXPECT_THROW({Cursor(iobuf1.get()).pull(buf, 20);},
211 RWPrivateCursor cursor(iobuf1.get());
212 auto p = cursor.peek();
213 EXPECT_EQ("he", std::string(reinterpret_cast<const char*>(p.first),
215 cursor.skip(p.second);
217 EXPECT_EQ("llo ", std::string(reinterpret_cast<const char*>(p.first),
219 cursor.skip(p.second);
221 EXPECT_EQ("world", std::string(reinterpret_cast<const char*>(p.first),
223 cursor.skip(p.second);
224 EXPECT_EQ(3, iobuf1->countChainElements());
225 EXPECT_EQ(11, iobuf1->computeChainDataLength());
229 RWPrivateCursor cursor(iobuf1.get());
231 auto p = cursor.peek();
232 EXPECT_EQ("hello world", std::string(reinterpret_cast<const
233 char*>(p.first), p.second));
234 EXPECT_EQ(1, iobuf1->countChainElements());
235 EXPECT_EQ(11, iobuf1->computeChainDataLength());
239 TEST(IOBuf, Gather) {
240 std::unique_ptr<IOBuf> iobuf1(IOBuf::create(10));
241 append(iobuf1, "he");
242 std::unique_ptr<IOBuf> iobuf2(IOBuf::create(10));
243 append(iobuf2, "llo ");
244 std::unique_ptr<IOBuf> iobuf3(IOBuf::create(10));
245 append(iobuf3, "world");
246 iobuf1->prependChain(std::move(iobuf2));
247 iobuf1->prependChain(std::move(iobuf3));
248 EXPECT_EQ(3, iobuf1->countChainElements());
249 EXPECT_EQ(11, iobuf1->computeChainDataLength());
251 // Attempting to gather() more data than available in the chain should fail.
252 // Try from the very beginning of the chain.
253 RWPrivateCursor cursor(iobuf1.get());
254 EXPECT_THROW(cursor.gather(15), std::overflow_error);
255 // Now try from the middle of the chain
257 EXPECT_THROW(cursor.gather(10), std::overflow_error);
259 // Calling gatherAtMost() should succeed, however, and just gather
261 cursor.gatherAtMost(10);
262 EXPECT_EQ(8, cursor.length());
263 EXPECT_EQ(8, cursor.totalLength());
264 EXPECT_EQ("lo world",
265 folly::StringPiece(reinterpret_cast<const char*>(cursor.data()),
267 EXPECT_EQ(2, iobuf1->countChainElements());
268 EXPECT_EQ(11, iobuf1->computeChainDataLength());
270 // Now try gather again on the chain head
271 cursor = RWPrivateCursor(iobuf1.get());
273 // Since gather() doesn't split buffers, everything should be collapsed into
274 // a single buffer now.
275 EXPECT_EQ(1, iobuf1->countChainElements());
276 EXPECT_EQ(11, iobuf1->computeChainDataLength());
277 EXPECT_EQ(11, cursor.length());
278 EXPECT_EQ(11, cursor.totalLength());
281 TEST(IOBuf, cloneAndInsert) {
282 std::unique_ptr<IOBuf> iobuf1(IOBuf::create(10));
283 append(iobuf1, "he");
284 std::unique_ptr<IOBuf> iobuf2(IOBuf::create(10));
285 append(iobuf2, "llo ");
286 std::unique_ptr<IOBuf> iobuf3(IOBuf::create(10));
287 append(iobuf3, "world");
288 iobuf1->prependChain(std::move(iobuf2));
289 iobuf1->prependChain(std::move(iobuf3));
290 EXPECT_EQ(3, iobuf1->countChainElements());
291 EXPECT_EQ(11, iobuf1->computeChainDataLength());
293 std::unique_ptr<IOBuf> cloned;
295 Cursor(iobuf1.get()).clone(cloned, 3);
296 EXPECT_EQ(2, cloned->countChainElements());
297 EXPECT_EQ(3, cloned->computeChainDataLength());
300 EXPECT_EQ(11, Cursor(iobuf1.get()).cloneAtMost(cloned, 20));
301 EXPECT_EQ(3, cloned->countChainElements());
302 EXPECT_EQ(11, cloned->computeChainDataLength());
305 EXPECT_THROW({Cursor(iobuf1.get()).clone(cloned, 20);},
309 // Check that inserting in the middle of an iobuf splits
310 RWPrivateCursor cursor(iobuf1.get());
311 Cursor(iobuf1.get()).clone(cloned, 3);
312 EXPECT_EQ(2, cloned->countChainElements());
313 EXPECT_EQ(3, cloned->computeChainDataLength());
317 cursor.insert(std::move(cloned));
318 cursor.insert(folly::IOBuf::create(0));
319 EXPECT_EQ(7, iobuf1->countChainElements());
320 EXPECT_EQ(14, iobuf1->computeChainDataLength());
321 // Check that nextBuf got set correctly to the buffer with 1 byte left
322 EXPECT_EQ(1, cursor.peek().second);
323 cursor.read<uint8_t>();
327 // Check that inserting at the end doesn't create empty buf
328 RWPrivateCursor cursor(iobuf1.get());
329 Cursor(iobuf1.get()).clone(cloned, 1);
330 EXPECT_EQ(1, cloned->countChainElements());
331 EXPECT_EQ(1, cloned->computeChainDataLength());
335 cursor.insert(std::move(cloned));
336 EXPECT_EQ(8, iobuf1->countChainElements());
337 EXPECT_EQ(15, iobuf1->computeChainDataLength());
338 // Check that nextBuf got set correctly
339 cursor.read<uint8_t>();
342 // Check that inserting at the beginning doesn't create empty buf
343 RWPrivateCursor cursor(iobuf1.get());
344 Cursor(iobuf1.get()).clone(cloned, 1);
345 EXPECT_EQ(1, cloned->countChainElements());
346 EXPECT_EQ(1, cloned->computeChainDataLength());
348 cursor.insert(std::move(cloned));
349 EXPECT_EQ(9, iobuf1->countChainElements());
350 EXPECT_EQ(16, iobuf1->computeChainDataLength());
351 // Check that nextBuf got set correctly
352 cursor.read<uint8_t>();
356 TEST(IOBuf, Appender) {
357 std::unique_ptr<IOBuf> head(IOBuf::create(10));
358 append(head, "hello");
360 Appender app(head.get(), 10);
361 uint32_t cap = head->capacity();
362 uint32_t len1 = app.length();
363 EXPECT_EQ(cap - 5, len1);
364 app.ensure(len1); // won't grow
365 EXPECT_EQ(len1, app.length());
366 app.ensure(len1 + 1); // will grow
367 EXPECT_LE(len1 + 1, app.length());
369 append(app, " world");
370 EXPECT_EQ("hello world", toString(*head));
373 TEST(IOBuf, QueueAppender) {
374 folly::IOBufQueue queue;
376 // Allocate 100 bytes at once, but don't grow past 1024
377 QueueAppender app(&queue, 100);
378 size_t n = 1024 / sizeof(uint32_t);
379 for (uint32_t i = 0; i < n; ++i) {
383 // There must be a goodMallocSize between 100 and 1024...
384 EXPECT_LT(1, queue.front()->countChainElements());
385 const IOBuf* buf = queue.front();
387 EXPECT_LE(100, buf->capacity());
389 } while (buf != queue.front());
391 Cursor cursor(queue.front());
392 for (uint32_t i = 0; i < n; ++i) {
393 EXPECT_EQ(i, cursor.readBE<uint32_t>());
396 EXPECT_THROW({cursor.readBE<uint32_t>();}, std::out_of_range);
399 TEST(IOBuf, CursorOperators) {
400 // Test operators on a single-item chain
402 std::unique_ptr<IOBuf> chain1(IOBuf::create(20));
405 Cursor curs1(chain1.get());
406 EXPECT_EQ(0, curs1 - chain1.get());
408 EXPECT_EQ(3, curs1 - chain1.get());
410 EXPECT_EQ(10, curs1 - chain1.get());
412 Cursor curs2(chain1.get());
413 EXPECT_EQ(0, curs2 - chain1.get());
414 EXPECT_EQ(10, curs1 - curs2);
415 EXPECT_THROW(curs2 - curs1, std::out_of_range);
418 // Test cross-chain operations
420 std::unique_ptr<IOBuf> chain1(IOBuf::create(20));
422 std::unique_ptr<IOBuf> chain2 = chain1->clone();
424 Cursor curs1(chain1.get());
425 Cursor curs2(chain2.get());
426 EXPECT_THROW(curs1 - curs2, std::out_of_range);
427 EXPECT_THROW(curs1 - chain2.get(), std::out_of_range);
430 // Test operations on multi-item chains
432 std::unique_ptr<IOBuf> chain(IOBuf::create(20));
434 chain->appendChain(chain->clone());
435 EXPECT_EQ(20, chain->computeChainDataLength());
437 Cursor curs1(chain.get());
439 Cursor curs2(chain.get());
441 EXPECT_EQ(2, curs1 - curs2);
442 EXPECT_EQ(5, curs1 - chain.get());
443 EXPECT_THROW(curs2 - curs1, std::out_of_range);
446 EXPECT_EQ(9, curs1 - curs2);
447 EXPECT_EQ(12, curs1 - chain.get());
448 EXPECT_THROW(curs2 - curs1, std::out_of_range);
451 EXPECT_EQ(2, curs1 - curs2);
452 EXPECT_THROW(curs2 - curs1, std::out_of_range);
456 TEST(IOBuf, StringOperations) {
457 // Test a single buffer with two null-terminated strings and an extra uint8_t
460 std::unique_ptr<IOBuf> chain(IOBuf::create(16));
461 Appender app(chain.get(), 0);
462 app.push(reinterpret_cast<const uint8_t*>("hello\0world\0\x01"), 13);
464 Cursor curs(chain.get());
465 EXPECT_STREQ("hello", curs.readTerminatedString().c_str());
466 EXPECT_STREQ("world", curs.readTerminatedString().c_str());
467 EXPECT_EQ(1, curs.read<uint8_t>());
470 // Test multiple buffers where the first is empty and the string starts in
471 // the second buffer.
473 std::unique_ptr<IOBuf> chain(IOBuf::create(8));
474 chain->prependChain(IOBuf::create(12));
475 Appender app(chain.get(), 0);
476 app.push(reinterpret_cast<const uint8_t*>("hello world\0"), 12);
478 Cursor curs(chain.get());
479 EXPECT_STREQ("hello world", curs.readTerminatedString().c_str());
482 // Test multiple buffers with a single null-terminated string spanning them
484 std::unique_ptr<IOBuf> chain(IOBuf::create(8));
485 chain->prependChain(IOBuf::create(8));
487 chain->next()->append(4);
488 RWPrivateCursor rwc(chain.get());
489 rwc.push(reinterpret_cast<const uint8_t*>("hello world\0"), 12);
491 Cursor curs(chain.get());
492 EXPECT_STREQ("hello world", curs.readTerminatedString().c_str());
495 // Test a reading a null-terminated string that's longer than the maximum
498 std::unique_ptr<IOBuf> chain(IOBuf::create(16));
499 Appender app(chain.get(), 0);
500 app.push(reinterpret_cast<const uint8_t*>("hello world\0"), 12);
502 Cursor curs(chain.get());
503 EXPECT_THROW(curs.readTerminatedString('\0', 5), std::length_error);
506 // Test reading a null-terminated string from a chain with an empty buffer at
509 std::unique_ptr<IOBuf> buf(IOBuf::create(8));
510 Appender app(buf.get(), 0);
511 app.push(reinterpret_cast<const uint8_t*>("hello\0"), 6);
512 std::unique_ptr<IOBuf> chain(IOBuf::create(8));
513 chain->prependChain(std::move(buf));
515 Cursor curs(chain.get());
516 EXPECT_STREQ("hello", curs.readTerminatedString().c_str());
519 // Test reading a two fixed-length strings from a single buffer with an extra
520 // uint8_t at the end
522 std::unique_ptr<IOBuf> chain(IOBuf::create(16));
523 Appender app(chain.get(), 0);
524 app.push(reinterpret_cast<const uint8_t*>("helloworld\x01"), 11);
526 Cursor curs(chain.get());
527 EXPECT_STREQ("hello", curs.readFixedString(5).c_str());
528 EXPECT_STREQ("world", curs.readFixedString(5).c_str());
529 EXPECT_EQ(1, curs.read<uint8_t>());
532 // Test multiple buffers where the first is empty and a fixed-length string
533 // starts in the second buffer.
535 std::unique_ptr<IOBuf> chain(IOBuf::create(8));
536 chain->prependChain(IOBuf::create(16));
537 Appender app(chain.get(), 0);
538 app.push(reinterpret_cast<const uint8_t*>("hello world"), 11);
540 Cursor curs(chain.get());
541 EXPECT_STREQ("hello world", curs.readFixedString(11).c_str());
544 // Test multiple buffers with a single fixed-length string spanning them
546 std::unique_ptr<IOBuf> chain(IOBuf::create(8));
547 chain->prependChain(IOBuf::create(8));
549 chain->next()->append(4);
550 RWPrivateCursor rwc(chain.get());
551 rwc.push(reinterpret_cast<const uint8_t*>("hello world"), 11);
553 Cursor curs(chain.get());
554 EXPECT_STREQ("hello world", curs.readFixedString(11).c_str());
557 // Test reading a fixed-length string from a chain with an empty buffer at
560 std::unique_ptr<IOBuf> buf(IOBuf::create(8));
561 Appender app(buf.get(), 0);
562 app.push(reinterpret_cast<const uint8_t*>("hello"), 5);
563 std::unique_ptr<IOBuf> chain(IOBuf::create(8));
564 chain->prependChain(std::move(buf));
566 Cursor curs(chain.get());
567 EXPECT_STREQ("hello", curs.readFixedString(5).c_str());
571 int benchmark_size = 1000;
572 unique_ptr<IOBuf> iobuf_benchmark;
574 unique_ptr<IOBuf> iobuf_read_benchmark;
576 template <class CursClass>
577 void runBenchmark() {
578 CursClass c(iobuf_benchmark.get());
580 for(int i = 0; i < benchmark_size; i++) {
585 BENCHMARK(rwPrivateCursorBenchmark, iters) {
587 runBenchmark<RWPrivateCursor>();
591 BENCHMARK(rwUnshareCursorBenchmark, iters) {
593 runBenchmark<RWUnshareCursor>();
598 BENCHMARK(cursorBenchmark, iters) {
600 Cursor c(iobuf_read_benchmark.get());
601 for(int i = 0; i < benchmark_size ; i++) {
607 BENCHMARK(skipBenchmark, iters) {
610 Cursor c(iobuf_read_benchmark.get());
611 for(int i = 0; i < benchmark_size ; i++) {
619 // _bin/folly/experimental/io/test/iobuf_cursor_test -benchmark
621 // Benchmark Iters Total t t/iter iter/sec
622 // ---------------------------------------------------------------------------
623 // rwPrivateCursorBenchmark 100000 142.9 ms 1.429 us 683.5 k
624 // rwUnshareCursorBenchmark 100000 309.3 ms 3.093 us 315.7 k
625 // cursorBenchmark 100000 741.4 ms 7.414 us 131.7 k
626 // skipBenchmark 100000 738.9 ms 7.389 us 132.2 k
630 // Linux dev2159.snc6.facebook.com 2.6.33-7_fbk15_104e4d0 #1 SMP
631 // Tue Oct 19 22:40:30 PDT 2010 x86_64 x86_64 x86_64 GNU/Linux
633 // 72GB RAM, 2 CPUs (Intel(R) Xeon(R) CPU L5630 @ 2.13GHz)
634 // hyperthreading disabled
636 int main(int argc, char** argv) {
637 testing::InitGoogleTest(&argc, argv);
638 google::ParseCommandLineFlags(&argc, &argv, true);
640 auto ret = RUN_ALL_TESTS();
642 if (ret == 0 && FLAGS_benchmark) {
643 iobuf_benchmark = IOBuf::create(benchmark_size);
644 iobuf_benchmark->append(benchmark_size);
646 iobuf_read_benchmark = IOBuf::create(1);
647 for (int i = 0; i < benchmark_size; i++) {
648 unique_ptr<IOBuf> iobuf2(IOBuf::create(1));
650 iobuf_read_benchmark->prependChain(std::move(iobuf2));
653 folly::runBenchmarks();