| // 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 "base/strings/stringprintf.h" | |
| #include <errno.h> | |
| #include <stddef.h> | |
| #include <iterator> | |
| #include <vector> | |
| #include "base/macros.h" | |
| #include "base/scoped_clear_errno.h" | |
| #include "base/strings/string_util.h" | |
| #include "base/strings/utf_string_conversions.h" | |
| #include "util/build_config.h" | |
| namespace base { | |
| namespace { | |
| // Overloaded wrappers around vsnprintf and vswprintf. The buf_size parameter | |
| // is the size of the buffer. These return the number of characters in the | |
| // formatted string excluding the NUL terminator. If the buffer is not | |
| // large enough to accommodate the formatted string without truncation, they | |
| // return the number of characters that would be in the fully-formatted string | |
| // (vsnprintf, and vswprintf on Windows), or -1 (vswprintf on POSIX platforms). | |
| inline int vsnprintfT(char* buffer, | |
| size_t buf_size, | |
| const char* format, | |
| va_list argptr) { | |
| return base::vsnprintf(buffer, buf_size, format, argptr); | |
| } | |
| // Templatized backend for StringPrintF/StringAppendF. This does not finalize | |
| // the va_list, the caller is expected to do that. | |
| template <class StringType> | |
| static void StringAppendVT(StringType* dst, | |
| const typename StringType::value_type* format, | |
| va_list ap) { | |
| // First try with a small fixed size buffer. | |
| // This buffer size should be kept in sync with StringUtilTest.GrowBoundary | |
| // and StringUtilTest.StringPrintfBounds. | |
| typename StringType::value_type stack_buf[1024]; | |
| va_list ap_copy; | |
| va_copy(ap_copy, ap); | |
| #if !defined(OS_WIN) | |
| ScopedClearErrno clear_errno; | |
| #endif | |
| int result = vsnprintfT(stack_buf, std::size(stack_buf), format, ap_copy); | |
| va_end(ap_copy); | |
| if (result >= 0 && result < static_cast<int>(std::size(stack_buf))) { | |
| // It fit. | |
| dst->append(stack_buf, result); | |
| return; | |
| } | |
| // Repeatedly increase buffer size until it fits. | |
| int mem_length = std::size(stack_buf); | |
| while (true) { | |
| if (result < 0) { | |
| #if defined(OS_WIN) | |
| // On Windows, vsnprintfT always returns the number of characters in a | |
| // fully-formatted string, so if we reach this point, something else is | |
| // wrong and no amount of buffer-doubling is going to fix it. | |
| return; | |
| #else | |
| if (errno != 0 && errno != EOVERFLOW) | |
| return; | |
| // Try doubling the buffer size. | |
| mem_length *= 2; | |
| #endif | |
| } else { | |
| // We need exactly "result + 1" characters. | |
| mem_length = result + 1; | |
| } | |
| if (mem_length > 32 * 1024 * 1024) { | |
| // That should be plenty, don't try anything larger. This protects | |
| // against huge allocations when using vsnprintfT implementations that | |
| // return -1 for reasons other than overflow without setting errno. | |
| DLOG(WARNING) << "Unable to printf the requested string due to size."; | |
| return; | |
| } | |
| std::vector<typename StringType::value_type> mem_buf(mem_length); | |
| // NOTE: You can only use a va_list once. Since we're in a while loop, we | |
| // need to make a new copy each time so we don't use up the original. | |
| va_copy(ap_copy, ap); | |
| result = vsnprintfT(&mem_buf[0], mem_length, format, ap_copy); | |
| va_end(ap_copy); | |
| if ((result >= 0) && (result < mem_length)) { | |
| // It fit. | |
| dst->append(&mem_buf[0], result); | |
| return; | |
| } | |
| } | |
| } | |
| } // namespace | |
| std::string StringPrintf(const char* format, ...) { | |
| va_list ap; | |
| va_start(ap, format); | |
| std::string result; | |
| StringAppendV(&result, format, ap); | |
| va_end(ap); | |
| return result; | |
| } | |
| std::string StringPrintV(const char* format, va_list ap) { | |
| std::string result; | |
| StringAppendV(&result, format, ap); | |
| return result; | |
| } | |
| const std::string& SStringPrintf(std::string* dst, const char* format, ...) { | |
| va_list ap; | |
| va_start(ap, format); | |
| dst->clear(); | |
| StringAppendV(dst, format, ap); | |
| va_end(ap); | |
| return *dst; | |
| } | |
| void StringAppendF(std::string* dst, const char* format, ...) { | |
| va_list ap; | |
| va_start(ap, format); | |
| StringAppendV(dst, format, ap); | |
| va_end(ap); | |
| } | |
| void StringAppendV(std::string* dst, const char* format, va_list ap) { | |
| StringAppendVT(dst, format, ap); | |
| } | |
| } // namespace base |