Index: mojo/edk/util/string_printf.cc |
diff --git a/mojo/edk/util/string_printf.cc b/mojo/edk/util/string_printf.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..ba762ee287024a3cc4ee9e288555229bdada5f6e |
--- /dev/null |
+++ b/mojo/edk/util/string_printf.cc |
@@ -0,0 +1,92 @@ |
+// Copyright 2013 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "mojo/edk/util/string_printf.h" |
+ |
+#include <assert.h> |
+#include <errno.h> |
+#include <stdarg.h> |
+#include <stddef.h> |
+#include <stdio.h> |
+ |
+#include <memory> |
+ |
+namespace mojo { |
+namespace util { |
+namespace { |
+ |
+void StringVAppendfHelper(std::string* dest, const char* format, va_list ap) { |
+ // Size of the small stack buffer to use first. This should be kept in sync |
+ // with the numbers in StringPrintfTest.StringPrintf_Boundary. |
+ constexpr size_t kStackBufferSize = 1024u; |
+ |
+ // First, try with a small buffer on the stack. |
+ char stack_buf[kStackBufferSize]; |
+ // Copy |ap| (which can only be used once), in case we need to retry. |
+ va_list ap_copy; |
+ va_copy(ap_copy, ap); |
+ int result = vsnprintf(stack_buf, kStackBufferSize, format, ap_copy); |
+ va_end(ap_copy); |
+ if (result < 0) { |
+ // As far as I can tell, we'd only get |EOVERFLOW| if the result is so large |
+ // that it can't be represented by an |int| (in which case retrying would be |
+ // futile), so Chromium's implementation is wrong. |
+ return; |
+ } |
+ // |result| should be the number of characters we need, not including the |
+ // terminating null. However, |vsnprintf()| always null-terminates! |
+ size_t output_size = static_cast<size_t>(result); |
+ // Check if the output fit into our stack buffer. This is "<" not "<=", since |
+ // |vsnprintf()| will null-terminate. |
+ if (output_size < kStackBufferSize) { |
+ // It fit. |
+ dest->append(stack_buf, static_cast<size_t>(result)); |
+ return; |
+ } |
+ |
+ // Since we have the required output size, we can just heap allocate that. |
+ // (Add 1 because |vsnprintf()| will always null-terminate.) |
+ size_t heap_buf_size = output_size + 1u; |
+ std::unique_ptr<char[]> heap_buf(new char[heap_buf_size]); |
+ result = vsnprintf(heap_buf.get(), heap_buf_size, format, ap); |
+ if (result < 0 || static_cast<size_t>(result) > output_size) { |
+ assert(false); |
+ return; |
+ } |
+ assert(static_cast<size_t>(result) == output_size); |
+ dest->append(heap_buf.get(), static_cast<size_t>(result)); |
+} |
+ |
+} // namespace |
+ |
+std::string StringPrintf(const char* format, ...) { |
+ va_list ap; |
+ va_start(ap, format); |
+ std::string rv; |
+ StringVAppendf(&rv, format, ap); |
+ va_end(ap); |
+ return rv; |
+} |
+ |
+std::string StringVPrintf(const char* format, va_list ap) { |
+ std::string rv; |
+ StringVAppendf(&rv, format, ap); |
+ return rv; |
+} |
+ |
+void StringAppendf(std::string* dest, const char* format, ...) { |
+ va_list ap; |
+ va_start(ap, format); |
+ StringVAppendf(dest, format, ap); |
+ va_end(ap); |
+} |
+ |
+void StringVAppendf(std::string* dest, const char* format, va_list ap) { |
+ int old_errno = errno; |
+ StringVAppendfHelper(dest, format, ap); |
+ errno = old_errno; |
+} |
+ |
+} // namespace util |
+} // namespace mojo |