diff options
author | brettw@chromium.org <brettw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-08-17 03:45:37 +0000 |
---|---|---|
committer | brettw@chromium.org <brettw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-08-17 03:45:37 +0000 |
commit | e6811ed52ec6c97dfb6d9073c8841d7f67bba082 (patch) | |
tree | 3d6a1abc880b89e94382f9f7c8e028d765f33508 /base | |
parent | e486aef731a84a591451ec7e92e79724757a737d (diff) | |
download | chromium_src-e6811ed52ec6c97dfb6d9073c8841d7f67bba082.zip chromium_src-e6811ed52ec6c97dfb6d9073c8841d7f67bba082.tar.gz chromium_src-e6811ed52ec6c97dfb6d9073c8841d7f67bba082.tar.bz2 |
Move StringPrintf into its own file and use the base namespace. Currently this has using
directives for the functions so I don't have to change all files to use the namespace.
No code changes to logic.
TEST=it compiles + the included unit tests
BUG=none
Review URL: http://codereview.chromium.org/3181016
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@56299 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'base')
-rw-r--r-- | base/base.gyp | 1 | ||||
-rw-r--r-- | base/base.gypi | 2 | ||||
-rw-r--r-- | base/string_util.cc | 157 | ||||
-rw-r--r-- | base/string_util.h | 32 | ||||
-rw-r--r-- | base/string_util_unittest.cc | 140 | ||||
-rw-r--r-- | base/stringprintf.cc | 175 | ||||
-rw-r--r-- | base/stringprintf.h | 56 | ||||
-rw-r--r-- | base/stringprintf_unittest.cc | 152 |
8 files changed, 391 insertions, 324 deletions
diff --git a/base/base.gyp b/base/base.gyp index f401bf3..195e73a 100644 --- a/base/base.gyp +++ b/base/base.gyp @@ -135,6 +135,7 @@ 'string_split_unittest.cc', 'string_tokenizer_unittest.cc', 'string_util_unittest.cc', + 'stringprintf_unittest.cc', 'sys_info_unittest.cc', 'sys_string_conversions_mac_unittest.mm', 'sys_string_conversions_unittest.cc', diff --git a/base/base.gypi b/base/base.gypi index a307820..09dcdff 100644 --- a/base/base.gypi +++ b/base/base.gypi @@ -228,6 +228,8 @@ 'string_util.cc', 'string_util.h', 'string_util_win.h', + 'stringprintf.cc', + 'stringprintf.h', 'sys_info.h', 'sys_info_chromeos.cc', 'sys_info_freebsd.cc', diff --git a/base/string_util.cc b/base/string_util.cc index 8d2413b..917d9a9 100644 --- a/base/string_util.cc +++ b/base/string_util.cc @@ -757,163 +757,6 @@ void ReplaceSubstringsAfterOffset(std::string* str, true); // replace all instances } -// 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); -} - -inline int vsnprintfT(wchar_t* buffer, - size_t buf_size, - const wchar_t* format, - va_list argptr) { - return base::vswprintf(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; - GG_VA_COPY(ap_copy, ap); - -#if !defined(OS_WIN) - errno = 0; -#endif - int result = vsnprintfT(stack_buf, arraysize(stack_buf), format, ap_copy); - va_end(ap_copy); - - if (result >= 0 && result < static_cast<int>(arraysize(stack_buf))) { - // It fit. - dst->append(stack_buf, result); - return; - } - - // Repeatedly increase buffer size until it fits. - int mem_length = arraysize(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. - if (errno != 0 && errno != EOVERFLOW) -#endif - { - // If an error other than overflow occurred, it's never going to work. - DLOG(WARNING) << "Unable to printf the requested string due to error."; - return; - } - // Try doubling the buffer size. - mem_length *= 2; - } 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. - GG_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; - } - } -} - -void StringAppendV(std::string* dst, const char* format, va_list ap) { - StringAppendVT(dst, format, ap); -} - -void StringAppendV(std::wstring* dst, const wchar_t* format, va_list ap) { - StringAppendVT(dst, format, ap); -} - -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::wstring StringPrintf(const wchar_t* format, ...) { - va_list ap; - va_start(ap, format); - std::wstring 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; -} - -const std::wstring& SStringPrintf(std::wstring* dst, - const wchar_t* 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 StringAppendF(std::wstring* dst, const wchar_t* format, ...) { - va_list ap; - va_start(ap, format); - StringAppendV(dst, format, ap); - va_end(ap); -} - template<typename STR> static void SplitStringT(const STR& str, const typename STR::value_type s, diff --git a/base/string_util.h b/base/string_util.h index 9edf85f..c69b2de 100644 --- a/base/string_util.h +++ b/base/string_util.h @@ -18,6 +18,11 @@ #include "base/string16.h" #include "base/string_piece.h" // For implicit conversions. +// TODO(brettw) remove this dependency. Previously StringPrintf lived in this +// file. We need to convert the callers over to using stringprintf.h instead +// and then remove this. +#include "base/stringprintf.h" + // Safe standard library wrappers for all platforms. namespace base { @@ -449,33 +454,6 @@ void ReplaceSubstringsAfterOffset(std::string* str, const std::string& find_this, const std::string& replace_with); -// Return a C++ string given printf-like input. -std::string StringPrintf(const char* format, ...) PRINTF_FORMAT(1, 2); -std::wstring StringPrintf(const wchar_t* format, ...) WPRINTF_FORMAT(1, 2); - -// Return a C++ string given vprintf-like input. -std::string StringPrintV(const char* format, va_list ap) PRINTF_FORMAT(1, 0); - -// Store result into a supplied string and return it -const std::string& SStringPrintf(std::string* dst, const char* format, ...) - PRINTF_FORMAT(2, 3); -const std::wstring& SStringPrintf(std::wstring* dst, - const wchar_t* format, ...) - WPRINTF_FORMAT(2, 3); - -// Append result to a supplied string -void StringAppendF(std::string* dst, const char* format, ...) - PRINTF_FORMAT(2, 3); -void StringAppendF(std::wstring* dst, const wchar_t* format, ...) - WPRINTF_FORMAT(2, 3); - -// Lower-level routine that takes a va_list and appends to a specified -// string. All other routines are just convenience wrappers around it. -void StringAppendV(std::string* dst, const char* format, va_list ap) - PRINTF_FORMAT(2, 0); -void StringAppendV(std::wstring* dst, const wchar_t* format, va_list ap) - WPRINTF_FORMAT(2, 0); - // This is mpcomplete's pattern for saving a string copy when dealing with // a function that writes results into a wchar_t[] and wanting the result to // end up in a std::wstring. It ensures that the std::wstring's internal diff --git a/base/string_util_unittest.cc b/base/string_util_unittest.cc index 4d3fc1c..9f72b1f 100644 --- a/base/string_util_unittest.cc +++ b/base/string_util_unittest.cc @@ -693,146 +693,6 @@ TEST(StringUtilTest, VAList) { VariableArgsFunc("%d %d %s %lf", 45, 92, "This is interesting", 9.21); } -TEST(StringUtilTest, StringPrintfEmpty) { - EXPECT_EQ("", StringPrintf("%s", "")); -} - -TEST(StringUtilTest, StringPrintfMisc) { - EXPECT_EQ("123hello w", StringPrintf("%3d%2s %1c", 123, "hello", 'w')); - EXPECT_EQ(L"123hello w", StringPrintf(L"%3d%2ls %1lc", 123, L"hello", 'w')); -} - -TEST(StringUtilTest, StringAppendfEmptyString) { - std::string value("Hello"); - StringAppendF(&value, "%s", ""); - EXPECT_EQ("Hello", value); - - std::wstring valuew(L"Hello"); - StringAppendF(&valuew, L"%ls", L""); - EXPECT_EQ(L"Hello", valuew); -} - -TEST(StringUtilTest, StringAppendfString) { - std::string value("Hello"); - StringAppendF(&value, " %s", "World"); - EXPECT_EQ("Hello World", value); - - std::wstring valuew(L"Hello"); - StringAppendF(&valuew, L" %ls", L"World"); - EXPECT_EQ(L"Hello World", valuew); -} - -TEST(StringUtilTest, StringAppendfInt) { - std::string value("Hello"); - StringAppendF(&value, " %d", 123); - EXPECT_EQ("Hello 123", value); - - std::wstring valuew(L"Hello"); - StringAppendF(&valuew, L" %d", 123); - EXPECT_EQ(L"Hello 123", valuew); -} - -// Make sure that lengths exactly around the initial buffer size are handled -// correctly. -TEST(StringUtilTest, StringPrintfBounds) { - const int kSrcLen = 1026; - char src[kSrcLen]; - for (size_t i = 0; i < arraysize(src); i++) - src[i] = 'A'; - - wchar_t srcw[kSrcLen]; - for (size_t i = 0; i < arraysize(srcw); i++) - srcw[i] = 'A'; - - for (int i = 1; i < 3; i++) { - src[kSrcLen - i] = 0; - std::string out; - SStringPrintf(&out, "%s", src); - EXPECT_STREQ(src, out.c_str()); - - srcw[kSrcLen - i] = 0; - std::wstring outw; - SStringPrintf(&outw, L"%ls", srcw); - EXPECT_STREQ(srcw, outw.c_str()); - } -} - -// Test very large sprintfs that will cause the buffer to grow. -TEST(StringUtilTest, Grow) { - char src[1026]; - for (size_t i = 0; i < arraysize(src); i++) - src[i] = 'A'; - src[1025] = 0; - - const char* fmt = "%sB%sB%sB%sB%sB%sB%s"; - - std::string out; - SStringPrintf(&out, fmt, src, src, src, src, src, src, src); - - const int kRefSize = 320000; - char* ref = new char[kRefSize]; -#if defined(OS_WIN) - sprintf_s(ref, kRefSize, fmt, src, src, src, src, src, src, src); -#elif defined(OS_POSIX) - snprintf(ref, kRefSize, fmt, src, src, src, src, src, src, src); -#endif - - EXPECT_STREQ(ref, out.c_str()); - delete[] ref; -} - -// A helper for the StringAppendV test that follows. -// Just forwards its args to StringAppendV. -static void StringAppendVTestHelper(std::string* out, - const char* format, - ...) PRINTF_FORMAT(2, 3); - -static void StringAppendVTestHelper(std::string* out, const char* format, ...) { - va_list ap; - va_start(ap, format); - StringAppendV(out, format, ap); - va_end(ap); -} - -TEST(StringUtilTest, StringAppendV) { - std::string out; - StringAppendVTestHelper(&out, "%d foo %s", 1, "bar"); - EXPECT_EQ("1 foo bar", out); -} - -// Test the boundary condition for the size of the string_util's -// internal buffer. -TEST(StringUtilTest, GrowBoundary) { - const int string_util_buf_len = 1024; - // Our buffer should be one larger than the size of StringAppendVT's stack - // buffer. - const int buf_len = string_util_buf_len + 1; - char src[buf_len + 1]; // Need extra one for NULL-terminator. - for (int i = 0; i < buf_len; ++i) - src[i] = 'a'; - src[buf_len] = 0; - - std::string out; - SStringPrintf(&out, "%s", src); - - EXPECT_STREQ(src, out.c_str()); -} - -// TODO(evanm): what's the proper cross-platform test here? -#if defined(OS_WIN) -// sprintf in Visual Studio fails when given U+FFFF. This tests that the -// failure case is gracefuly handled. -TEST(StringUtilTest, Invalid) { - wchar_t invalid[2]; - invalid[0] = 0xffff; - invalid[1] = 0; - - std::wstring out; - SStringPrintf(&out, L"%ls", invalid); - EXPECT_STREQ(L"", out.c_str()); -} -#endif - // Test for SplitString TEST(StringUtilTest, SplitString) { std::vector<std::wstring> r; diff --git a/base/stringprintf.cc b/base/stringprintf.cc new file mode 100644 index 0000000..5607d39 --- /dev/null +++ b/base/stringprintf.cc @@ -0,0 +1,175 @@ +// Copyright (c) 2010 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/stringprintf.h" + +#include <errno.h> + +#include "base/string_util.h" +#include "base/utf_string_conversions.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); +} + +inline int vsnprintfT(wchar_t* buffer, + size_t buf_size, + const wchar_t* format, + va_list argptr) { + return base::vswprintf(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; + GG_VA_COPY(ap_copy, ap); + +#if !defined(OS_WIN) + errno = 0; +#endif + int result = vsnprintfT(stack_buf, arraysize(stack_buf), format, ap_copy); + va_end(ap_copy); + + if (result >= 0 && result < static_cast<int>(arraysize(stack_buf))) { + // It fit. + dst->append(stack_buf, result); + return; + } + + // Repeatedly increase buffer size until it fits. + int mem_length = arraysize(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. + if (errno != 0 && errno != EOVERFLOW) +#endif + { + // If an error other than overflow occurred, it's never going to work. + DLOG(WARNING) << "Unable to printf the requested string due to error."; + return; + } + // Try doubling the buffer size. + mem_length *= 2; + } 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. + GG_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::wstring StringPrintf(const wchar_t* format, ...) { + va_list ap; + va_start(ap, format); + std::wstring 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; +} + +const std::wstring& SStringPrintf(std::wstring* dst, + const wchar_t* 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 StringAppendF(std::wstring* dst, const wchar_t* 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); +} + +void StringAppendV(std::wstring* dst, const wchar_t* format, va_list ap) { + StringAppendVT(dst, format, ap); +} + +} // namespace base diff --git a/base/stringprintf.h b/base/stringprintf.h new file mode 100644 index 0000000..43d60d4 --- /dev/null +++ b/base/stringprintf.h @@ -0,0 +1,56 @@ +// Copyright (c) 2010 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. + +#ifndef BASE_STRINGPRINTF_H_ +#define BASE_STRINGPRINTF_H_ + +#include <stdarg.h> // va_list + +#include <string> + +#include "base/compiler_specific.h" + +namespace base { + +// Return a C++ string given printf-like input. +std::string StringPrintf(const char* format, ...) PRINTF_FORMAT(1, 2); +std::wstring StringPrintf(const wchar_t* format, ...) WPRINTF_FORMAT(1, 2); + +// Return a C++ string given vprintf-like input. +std::string StringPrintV(const char* format, va_list ap) PRINTF_FORMAT(1, 0); + +// Store result into a supplied string and return it. +const std::string& SStringPrintf(std::string* dst, const char* format, ...) + PRINTF_FORMAT(2, 3); +const std::wstring& SStringPrintf(std::wstring* dst, + const wchar_t* format, ...) + WPRINTF_FORMAT(2, 3); + +// Append result to a supplied string. +void StringAppendF(std::string* dst, const char* format, ...) + PRINTF_FORMAT(2, 3); +void StringAppendF(std::wstring* dst, const wchar_t* format, ...) + WPRINTF_FORMAT(2, 3); + +// Lower-level routine that takes a va_list and appends to a specified +// string. All other routines are just convenience wrappers around it. +void StringAppendV(std::string* dst, const char* format, va_list ap) + PRINTF_FORMAT(2, 0); +void StringAppendV(std::wstring* dst, const wchar_t* format, va_list ap) + WPRINTF_FORMAT(2, 0); + +} // namespace base + +// Don't require the namespace for legacy code. New code should use "base::" or +// have its own using decl. +// +// TODO(brettw) remove these when calling code is converted. +using base::StringPrintf; +using base::StringPrintV; +using base::SStringPrintf; +using base::StringAppendV; +using base::StringAppendF; +using base::StringAppendV; + +#endif // BASE_STRINGPRINTF_H_ diff --git a/base/stringprintf_unittest.cc b/base/stringprintf_unittest.cc new file mode 100644 index 0000000..ffb9c77 --- /dev/null +++ b/base/stringprintf_unittest.cc @@ -0,0 +1,152 @@ +// Copyright (c) 2010 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/basictypes.h" +#include "base/stringprintf.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace base { + +namespace { + +// A helper for the StringAppendV test that follows. +// +// Just forwards its args to StringAppendV. +static void StringAppendVTestHelper(std::string* out, const char* format, ...) { + va_list ap; + va_start(ap, format); + StringAppendV(out, format, ap); + va_end(ap); +} + +} // namespace + +TEST(StringPrintfTest, StringPrintfEmpty) { + EXPECT_EQ("", StringPrintf("%s", "")); +} + +TEST(StringPrintfTest, StringPrintfMisc) { + EXPECT_EQ("123hello w", StringPrintf("%3d%2s %1c", 123, "hello", 'w')); + EXPECT_EQ(L"123hello w", StringPrintf(L"%3d%2ls %1lc", 123, L"hello", 'w')); +} + +TEST(StringPrintfTest, StringAppendfEmptyString) { + std::string value("Hello"); + StringAppendF(&value, "%s", ""); + EXPECT_EQ("Hello", value); + + std::wstring valuew(L"Hello"); + StringAppendF(&valuew, L"%ls", L""); + EXPECT_EQ(L"Hello", valuew); +} + +TEST(StringPrintfTest, StringAppendfString) { + std::string value("Hello"); + StringAppendF(&value, " %s", "World"); + EXPECT_EQ("Hello World", value); + + std::wstring valuew(L"Hello"); + StringAppendF(&valuew, L" %ls", L"World"); + EXPECT_EQ(L"Hello World", valuew); +} + +TEST(StringPrintfTest, StringAppendfInt) { + std::string value("Hello"); + StringAppendF(&value, " %d", 123); + EXPECT_EQ("Hello 123", value); + + std::wstring valuew(L"Hello"); + StringAppendF(&valuew, L" %d", 123); + EXPECT_EQ(L"Hello 123", valuew); +} + +// Make sure that lengths exactly around the initial buffer size are handled +// correctly. +TEST(StringPrintfTest, StringPrintfBounds) { + const int kSrcLen = 1026; + char src[kSrcLen]; + for (size_t i = 0; i < arraysize(src); i++) + src[i] = 'A'; + + wchar_t srcw[kSrcLen]; + for (size_t i = 0; i < arraysize(srcw); i++) + srcw[i] = 'A'; + + for (int i = 1; i < 3; i++) { + src[kSrcLen - i] = 0; + std::string out; + SStringPrintf(&out, "%s", src); + EXPECT_STREQ(src, out.c_str()); + + srcw[kSrcLen - i] = 0; + std::wstring outw; + SStringPrintf(&outw, L"%ls", srcw); + EXPECT_STREQ(srcw, outw.c_str()); + } +} + +// Test very large sprintfs that will cause the buffer to grow. +TEST(StringPrintfTest, Grow) { + char src[1026]; + for (size_t i = 0; i < arraysize(src); i++) + src[i] = 'A'; + src[1025] = 0; + + const char* fmt = "%sB%sB%sB%sB%sB%sB%s"; + + std::string out; + SStringPrintf(&out, fmt, src, src, src, src, src, src, src); + + const int kRefSize = 320000; + char* ref = new char[kRefSize]; +#if defined(OS_WIN) + sprintf_s(ref, kRefSize, fmt, src, src, src, src, src, src, src); +#elif defined(OS_POSIX) + snprintf(ref, kRefSize, fmt, src, src, src, src, src, src, src); +#endif + + EXPECT_STREQ(ref, out.c_str()); + delete[] ref; +} + +TEST(StringPrintfTest, StringAppendV) { + std::string out; + StringAppendVTestHelper(&out, "%d foo %s", 1, "bar"); + EXPECT_EQ("1 foo bar", out); +} + +// Test the boundary condition for the size of the string_util's +// internal buffer. +TEST(StringPrintfTest, GrowBoundary) { + const int string_util_buf_len = 1024; + // Our buffer should be one larger than the size of StringAppendVT's stack + // buffer. + const int buf_len = string_util_buf_len + 1; + char src[buf_len + 1]; // Need extra one for NULL-terminator. + for (int i = 0; i < buf_len; ++i) + src[i] = 'a'; + src[buf_len] = 0; + + std::string out; + SStringPrintf(&out, "%s", src); + + EXPECT_STREQ(src, out.c_str()); +} + +// TODO(evanm): what's the proper cross-platform test here? +#if defined(OS_WIN) +// sprintf in Visual Studio fails when given U+FFFF. This tests that the +// failure case is gracefuly handled. +TEST(StringPrintfTest, Invalid) { + wchar_t invalid[2]; + invalid[0] = 0xffff; + invalid[1] = 0; + + std::wstring out; + SStringPrintf(&out, L"%ls", invalid); + EXPECT_STREQ(L"", out.c_str()); +} +#endif + +} // namespace base |