return Endian::little(read<T>());
}
+ /**
+ * Read a fixed-length string.
+ *
+ * The std::string-based APIs should probably be avoided unless you
+ * ultimately want the data to live in an std::string. You're better off
+ * using the pull() APIs to copy into a raw buffer otherwise.
+ */
+ std::string readFixedString(size_t len) {
+ std::string str;
+
+ str.reserve(len);
+ for (;;) {
+ // Fast path: it all fits in one buffer.
+ size_t available = length();
+ if (LIKELY(available >= len)) {
+ str.append(reinterpret_cast<const char*>(data()), len);
+ offset_ += len;
+ return str;
+ }
+
+ str.append(reinterpret_cast<const char*>(data()), available);
+ if (UNLIKELY(!tryAdvanceBuffer())) {
+ throw std::out_of_range("string underflow");
+ }
+ len -= available;
+ }
+ }
+
+ /**
+ * Read a string consisting of bytes until the given terminator character is
+ * seen. Raises an std::length_error if maxLength bytes have been processed
+ * before the terminator is seen.
+ *
+ * See comments in readFixedString() about when it's appropriate to use this
+ * vs. using pull().
+ */
+ std::string readTerminatedString(
+ char termChar = '\0',
+ size_t maxLength = std::numeric_limits<size_t>::max()) {
+ std::string str;
+
+ for (;;) {
+ const uint8_t* buf = data();
+ size_t buflen = length();
+
+ size_t i = 0;
+ while (i < buflen && buf[i] != termChar) {
+ ++i;
+
+ // Do this check after incrementing 'i', as even though we start at the
+ // 0 byte, it still represents a single character
+ if (str.length() + i >= maxLength) {
+ throw std::length_error("string overflow");
+ }
+ }
+
+ str.append(reinterpret_cast<const char*>(buf), i);
+ if (i < buflen) {
+ skip(i + 1);
+ return str;
+ }
+
+ skip(i);
+
+ if (UNLIKELY(!tryAdvanceBuffer())) {
+ throw std::out_of_range("string underflow");
+ }
+ }
+ }
+
explicit CursorBase(BufType* buf)
: crtBuf_(buf)
, offset_(0)
}
}
+TEST(IOBuf, StringOperations) {
+ // Test a single buffer with two null-terminated strings and an extra uint8_t
+ // at the end
+ {
+ std::unique_ptr<IOBuf> chain(IOBuf::create(16));
+ Appender app(chain.get(), 0);
+ app.pushAtMost(reinterpret_cast<const uint8_t*>("hello\0world\0\x01"), 13);
+
+ Cursor curs(chain.get());
+ EXPECT_STREQ("hello", curs.readTerminatedString().c_str());
+ EXPECT_STREQ("world", curs.readTerminatedString().c_str());
+ EXPECT_EQ(1, curs.read<uint8_t>());
+ }
+
+ // Test multiple buffers with a single null-terminated string spanning them
+ {
+ std::unique_ptr<IOBuf> chain(IOBuf::create(8));
+ chain->prependChain(IOBuf::create(8));
+ Appender app(chain.get(), 0);
+ app.pushAtMost(reinterpret_cast<const uint8_t*>("hello world\0"), 12);
+
+ Cursor curs(chain.get());
+ EXPECT_STREQ("hello world", curs.readTerminatedString().c_str());
+ }
+
+ // Test a reading a null-terminated string that's longer than the maximum
+ // allowable length
+ {
+ std::unique_ptr<IOBuf> chain(IOBuf::create(16));
+ Appender app(chain.get(), 0);
+ app.pushAtMost(reinterpret_cast<const uint8_t*>("hello world\0"), 12);
+
+ Cursor curs(chain.get());
+ EXPECT_THROW(curs.readTerminatedString('\0', 5), std::length_error);
+ }
+
+ // Test reading a null-termianted string from a chain with an empty buffer at
+ // the front
+ {
+ std::unique_ptr<IOBuf> buf(IOBuf::create(8));
+ Appender app(buf.get(), 0);
+ app.pushAtMost(reinterpret_cast<const uint8_t*>("hello\0"), 6);
+ std::unique_ptr<IOBuf> chain(IOBuf::create(8));
+ chain->prependChain(std::move(buf));
+
+ Cursor curs(chain.get());
+ EXPECT_STREQ("hello", curs.readTerminatedString().c_str());
+ }
+
+ // Test reading a two fixed-length strings from a single buffer with an extra
+ // uint8_t at the end
+ {
+ std::unique_ptr<IOBuf> chain(IOBuf::create(16));
+ Appender app(chain.get(), 0);
+ app.pushAtMost(reinterpret_cast<const uint8_t*>("helloworld\x01"), 11);
+
+ Cursor curs(chain.get());
+ EXPECT_STREQ("hello", curs.readFixedString(5).c_str());
+ EXPECT_STREQ("world", curs.readFixedString(5).c_str());
+ EXPECT_EQ(1, curs.read<uint8_t>());
+ }
+
+ // Test multiple buffers with a single fixed-length string spanning them
+ {
+ std::unique_ptr<IOBuf> chain(IOBuf::create(8));
+ chain->prependChain(IOBuf::create(8));
+ Appender app(chain.get(), 0);
+ app.pushAtMost(reinterpret_cast<const uint8_t*>("hello world"), 11);
+
+ Cursor curs(chain.get());
+ EXPECT_STREQ("hello world", curs.readFixedString(11).c_str());
+ }
+
+ // Test reading a fixed-length string from a chain with an empty buffer at
+ // the front
+ {
+ std::unique_ptr<IOBuf> buf(IOBuf::create(8));
+ Appender app(buf.get(), 0);
+ app.pushAtMost(reinterpret_cast<const uint8_t*>("hello"), 5);
+ std::unique_ptr<IOBuf> chain(IOBuf::create(8));
+ chain->prependChain(std::move(buf));
+
+ Cursor curs(chain.get());
+ EXPECT_STREQ("hello", curs.readFixedString(5).c_str());
+ }
+}
+
int benchmark_size = 1000;
unique_ptr<IOBuf> iobuf_benchmark;