--- /dev/null
+/*
+ * Copyright 2014 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <folly/io/Cursor.h>
+
+#include <cstdio>
+#include <folly/ScopeGuard.h>
+
+namespace folly { namespace io {
+
+void Appender::printf(const char* fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ vprintf(fmt, ap);
+ va_end(ap);
+}
+
+void Appender::vprintf(const char* fmt, va_list ap) {
+ // Make a copy of ap in case we need to retry.
+ // We use ap on the first attempt, so it always gets advanced
+ // passed the used arguments. We'll only use apCopy if we need to retry.
+ va_list apCopy;
+ va_copy(apCopy, ap);
+ SCOPE_EXIT {
+ va_end(apCopy);
+ };
+
+ // First try writing into our available data space.
+ int ret = vsnprintf(reinterpret_cast<char*>(writableData()), length(),
+ fmt, ap);
+ if (ret < 0) {
+ throw std::runtime_error("error formatting printf() data");
+ }
+ // vsnprintf() returns the number of characters that would be printed,
+ // not including the terminating nul.
+ if (ret < length()) {
+ // All of the data was successfully written.
+ append(ret);
+ return;
+ }
+
+ // There wasn't enough room for the data.
+ // Allocate more room, and then retry.
+ ensure(ret + 1);
+ ret = vsnprintf(reinterpret_cast<char*>(writableData()), length(),
+ fmt, apCopy);
+ if (ret < 0) {
+ throw std::runtime_error("error formatting printf() data");
+ }
+ if (ret >= length()) {
+ // This shouldn't ever happen.
+ throw std::runtime_error("unexpectedly out of buffer space on second "
+ "vsnprintf() attmept");
+ }
+ append(ret);
+}
+
+}} // folly::io
#define FOLLY_CURSOR_H
#include <assert.h>
+#include <cstdarg>
#include <stdexcept>
#include <string.h>
#include <type_traits>
#include <folly/io/IOBufQueue.h>
#include <folly/Likely.h>
#include <folly/Memory.h>
+#include <folly/Portability.h>
+#include <folly/Range.h>
/**
* Cursor class for fast iteration over IOBuf chains.
}
}
+ void push(ByteRange buf) {
+ if (this->pushAtMost(buf) != buf.size()) {
+ throw std::out_of_range("overflow");
+ }
+ }
+
+ size_t pushAtMost(ByteRange buf) {
+ Derived* d = static_cast<Derived*>(this);
+ return d->pushAtMost(buf.data(), buf.size());
+ }
+
/**
* push len bytes of data from input cursor, data could be in an IOBuf chain.
* If input cursor contains less than len bytes, or this cursor has less than
len -= available;
}
}
-
};
} // namespace detail
return this->crtBuf_->gather(this->offset_ + size);
}
+ using detail::Writable<RWCursor<access>>::pushAtMost;
size_t pushAtMost(const uint8_t* buf, size_t len) {
size_t copied = 0;
for (;;) {
crtBuf_ = buffer_->prev();
}
+ using detail::Writable<Appender>::pushAtMost;
size_t pushAtMost(const uint8_t* buf, size_t len) {
size_t copied = 0;
for (;;) {
}
}
+ /*
+ * Append to the end of this buffer, using a printf() style
+ * format specifier.
+ *
+ * Note that folly/Format.h provides nicer and more type-safe mechanisms
+ * for formatting strings, which should generally be preferred over
+ * printf-style formatting. Appender objects can be used directly as an
+ * output argument for Formatter objects. For example:
+ *
+ * Appender app(&iobuf);
+ * format("{} {}", "hello", "world")(app);
+ *
+ * However, printf-style strings are still needed when dealing with existing
+ * third-party code in some cases.
+ *
+ * This will always add a nul-terminating character after the end
+ * of the output. However, the buffer data length will only be updated to
+ * include the data itself. The nul terminator will be the first byte in the
+ * buffer tailroom.
+ *
+ * This method may throw exceptions on error.
+ */
+ void printf(FOLLY_PRINTF_FORMAT const char* fmt, ...)
+ FOLLY_PRINTF_FORMAT_ATTR(2, 3);
+
+ void vprintf(const char* fmt, va_list ap);
+
+ /*
+ * Calling an Appender object with a StringPiece will append the string
+ * piece. This allows Appender objects to be used directly with
+ * Formatter.
+ */
+ void operator()(StringPiece sp) {
+ push(ByteRange(sp));
+ }
+
private:
bool tryGrowChain() {
assert(crtBuf_->next() == buffer_);
queue_->postallocate(sizeof(T));
}
-
+ using detail::Writable<QueueAppender>::pushAtMost;
size_t pushAtMost(const uint8_t* buf, size_t len) {
size_t remaining = len;
while (remaining != 0) {
#include <boost/random.hpp>
#include <gtest/gtest.h>
#include <folly/Benchmark.h>
+#include <folly/Format.h>
#include <folly/Range.h>
#include <folly/io/Cursor.h>
+#include <folly/io/Cursor-defs.h>
DECLARE_bool(benchmark);
+using folly::ByteRange;
+using folly::format;
using folly::IOBuf;
+using folly::StringPiece;
using std::unique_ptr;
using namespace folly::io;
buf->append(data.size());
}
-void append(Appender& appender, folly::StringPiece data) {
- appender.push(reinterpret_cast<const uint8_t*>(data.data()), data.size());
+void append(Appender& appender, StringPiece data) {
+ appender.push(ByteRange(data));
}
std::string toString(const IOBuf& buf) {
EXPECT_EQ("hello world", toString(*head));
}
+TEST(IOBuf, Printf) {
+ IOBuf head(IOBuf::CREATE, 24);
+ Appender app(&head, 32);
+
+ app.printf("%s", "test");
+ EXPECT_EQ(head.length(), 4);
+ EXPECT_EQ(0, memcmp(head.data(), "test\0", 5));
+
+ app.printf("%d%s %s%s %#x", 32, "this string is",
+ "longer than our original allocation size,",
+ "and will therefore require a new allocation", 0x12345678);
+ // The tailroom should start with a nul byte now.
+ EXPECT_GE(head.prev()->tailroom(), 1);
+ EXPECT_EQ(0, *head.prev()->tail());
+
+ EXPECT_EQ("test32this string is longer than our original "
+ "allocation size,and will therefore require a "
+ "new allocation 0x12345678",
+ head.moveToFbString().toStdString());
+}
+
+TEST(IOBuf, Format) {
+ IOBuf head(IOBuf::CREATE, 24);
+ Appender app(&head, 32);
+
+ format("{}", "test")(app);
+ EXPECT_EQ(head.length(), 4);
+ EXPECT_EQ(0, memcmp(head.data(), "test", 4));
+
+ auto fmt = format("{}{} {}{} {:#x}",
+ 32, "this string is",
+ "longer than our original allocation size,",
+ "and will therefore require a new allocation",
+ 0x12345678);
+ fmt(app);
+ EXPECT_EQ("test32this string is longer than our original "
+ "allocation size,and will therefore require a "
+ "new allocation 0x12345678",
+ head.moveToFbString().toStdString());
+}
+
TEST(IOBuf, QueueAppender) {
folly::IOBufQueue queue;