/src/brpc/src/butil/strings/stringprintf.cc
Line | Count | Source |
1 | | // Copyright 2013 The Chromium Authors. All rights reserved. |
2 | | // Use of this source code is governed by a BSD-style license that can be |
3 | | // found in the LICENSE file. |
4 | | |
5 | | #include "butil/strings/stringprintf.h" |
6 | | |
7 | | #include <errno.h> |
8 | | |
9 | | #include "butil/scoped_clear_errno.h" |
10 | | #include "butil/strings/string_util.h" |
11 | | #include "butil/strings/utf_string_conversions.h" |
12 | | |
13 | | // gcc7 reports that the first arg to vsnprintfT in StringAppendVT is NULL, |
14 | | // which I can't figure out why, turn off the warning right now. |
15 | | #if defined(__GNUC__) && __GNUC__ >= 7 |
16 | | #pragma GCC diagnostic warning "-Wformat-truncation=0" |
17 | | #endif |
18 | | |
19 | | namespace butil { |
20 | | |
21 | | namespace { |
22 | | |
23 | | // Overloaded wrappers around vsnprintf and vswprintf. The buf_size parameter |
24 | | // is the size of the buffer. These return the number of characters in the |
25 | | // formatted string excluding the NUL terminator. If the buffer is not |
26 | | // large enough to accommodate the formatted string without truncation, they |
27 | | // return the number of characters that would be in the fully-formatted string |
28 | | // (vsnprintf, and vswprintf on Windows), or -1 (vswprintf on POSIX platforms). |
29 | | inline int vsnprintfT(char* buffer, |
30 | | size_t buf_size, |
31 | | const char* format, |
32 | 0 | va_list argptr) { |
33 | 0 | return butil::vsnprintf(buffer, buf_size, format, argptr); |
34 | 0 | } |
35 | | |
36 | | #if !defined(OS_ANDROID) |
37 | | inline int vsnprintfT(wchar_t* buffer, |
38 | | size_t buf_size, |
39 | | const wchar_t* format, |
40 | 0 | va_list argptr) { |
41 | 0 | return butil::vswprintf(buffer, buf_size, format, argptr); |
42 | 0 | } |
43 | | #endif |
44 | | |
45 | | // Templatized backend for StringPrintF/StringAppendF. This does not finalize |
46 | | // the va_list, the caller is expected to do that. |
47 | | template <class StringType> |
48 | | static void StringAppendVT(StringType* dst, |
49 | | const typename StringType::value_type* format, |
50 | 0 | va_list ap) { |
51 | | // First try with a small fixed size buffer. |
52 | | // This buffer size should be kept in sync with StringUtilTest.GrowBoundary |
53 | | // and StringUtilTest.StringPrintfBounds. |
54 | 0 | typename StringType::value_type stack_buf[1024]; |
55 | |
|
56 | 0 | va_list ap_copy; |
57 | 0 | GG_VA_COPY(ap_copy, ap); |
58 | |
|
59 | 0 | #if !defined(OS_WIN) |
60 | 0 | ScopedClearErrno clear_errno; |
61 | 0 | #endif |
62 | 0 | int result = vsnprintfT(stack_buf, arraysize(stack_buf), format, ap_copy); |
63 | 0 | va_end(ap_copy); |
64 | |
|
65 | 0 | if (result >= 0 && result < static_cast<int>(arraysize(stack_buf))) { |
66 | | // It fit. |
67 | 0 | dst->append(stack_buf, result); |
68 | 0 | return; |
69 | 0 | } |
70 | | |
71 | | // Repeatedly increase buffer size until it fits. |
72 | 0 | int mem_length = arraysize(stack_buf); |
73 | 0 | while (true) { |
74 | 0 | if (result < 0) { |
75 | | #if defined(OS_WIN) |
76 | | // On Windows, vsnprintfT always returns the number of characters in a |
77 | | // fully-formatted string, so if we reach this point, something else is |
78 | | // wrong and no amount of buffer-doubling is going to fix it. |
79 | | return; |
80 | | #else |
81 | 0 | if (errno != 0 && errno != EOVERFLOW) |
82 | 0 | return; |
83 | | // Try doubling the buffer size. |
84 | 0 | mem_length *= 2; |
85 | 0 | #endif |
86 | 0 | } else { |
87 | | // We need exactly "result + 1" characters. |
88 | 0 | mem_length = result + 1; |
89 | 0 | } |
90 | | |
91 | 0 | if (mem_length > 32 * 1024 * 1024) { |
92 | | // That should be plenty, don't try anything larger. This protects |
93 | | // against huge allocations when using vsnprintfT implementations that |
94 | | // return -1 for reasons other than overflow without setting errno. |
95 | 0 | DLOG(WARNING) << "Unable to printf the requested string due to size."; |
96 | 0 | return; |
97 | 0 | } |
98 | | |
99 | 0 | std::vector<typename StringType::value_type> mem_buf(mem_length); |
100 | | |
101 | | // NOTE: You can only use a va_list once. Since we're in a while loop, we |
102 | | // need to make a new copy each time so we don't use up the original. |
103 | 0 | GG_VA_COPY(ap_copy, ap); |
104 | 0 | result = vsnprintfT(&mem_buf[0], mem_length, format, ap_copy); |
105 | 0 | va_end(ap_copy); |
106 | |
|
107 | 0 | if ((result >= 0) && (result < mem_length)) { |
108 | | // It fit. |
109 | 0 | dst->append(&mem_buf[0], result); |
110 | 0 | return; |
111 | 0 | } |
112 | 0 | } |
113 | 0 | } Unexecuted instantiation: stringprintf.cc:void butil::(anonymous namespace)::StringAppendVT<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::value_type const*, __va_list_tag*) Unexecuted instantiation: stringprintf.cc:void butil::(anonymous namespace)::StringAppendVT<std::__cxx11::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> > >(std::__cxx11::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> >*, std::__cxx11::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> >::value_type const*, __va_list_tag*) |
114 | | |
115 | | } // namespace |
116 | | |
117 | 0 | std::string StringPrintf(const char* format, ...) { |
118 | 0 | va_list ap; |
119 | 0 | va_start(ap, format); |
120 | 0 | std::string result; |
121 | 0 | StringAppendV(&result, format, ap); |
122 | 0 | va_end(ap); |
123 | 0 | return result; |
124 | 0 | } |
125 | | |
126 | | #if !defined(OS_ANDROID) |
127 | 0 | std::wstring StringPrintf(const wchar_t* format, ...) { |
128 | 0 | va_list ap; |
129 | 0 | va_start(ap, format); |
130 | 0 | std::wstring result; |
131 | 0 | StringAppendV(&result, format, ap); |
132 | 0 | va_end(ap); |
133 | 0 | return result; |
134 | 0 | } |
135 | | #endif |
136 | | |
137 | 0 | std::string StringPrintV(const char* format, va_list ap) { |
138 | 0 | std::string result; |
139 | 0 | StringAppendV(&result, format, ap); |
140 | 0 | return result; |
141 | 0 | } |
142 | | |
143 | 0 | const std::string& SStringPrintf(std::string* dst, const char* format, ...) { |
144 | 0 | va_list ap; |
145 | 0 | va_start(ap, format); |
146 | 0 | dst->clear(); |
147 | 0 | StringAppendV(dst, format, ap); |
148 | 0 | va_end(ap); |
149 | 0 | return *dst; |
150 | 0 | } |
151 | | |
152 | | #if !defined(OS_ANDROID) |
153 | | const std::wstring& SStringPrintf(std::wstring* dst, |
154 | 0 | const wchar_t* format, ...) { |
155 | 0 | va_list ap; |
156 | 0 | va_start(ap, format); |
157 | 0 | dst->clear(); |
158 | 0 | StringAppendV(dst, format, ap); |
159 | 0 | va_end(ap); |
160 | 0 | return *dst; |
161 | 0 | } |
162 | | #endif |
163 | | |
164 | 0 | void StringAppendF(std::string* dst, const char* format, ...) { |
165 | 0 | va_list ap; |
166 | 0 | va_start(ap, format); |
167 | 0 | StringAppendV(dst, format, ap); |
168 | 0 | va_end(ap); |
169 | 0 | } |
170 | | |
171 | | #if !defined(OS_ANDROID) |
172 | 0 | void StringAppendF(std::wstring* dst, const wchar_t* format, ...) { |
173 | 0 | va_list ap; |
174 | 0 | va_start(ap, format); |
175 | 0 | StringAppendV(dst, format, ap); |
176 | 0 | va_end(ap); |
177 | 0 | } |
178 | | #endif |
179 | | |
180 | 0 | void StringAppendV(std::string* dst, const char* format, va_list ap) { |
181 | 0 | StringAppendVT(dst, format, ap); |
182 | 0 | } |
183 | | |
184 | | #if !defined(OS_ANDROID) |
185 | 0 | void StringAppendV(std::wstring* dst, const wchar_t* format, va_list ap) { |
186 | 0 | StringAppendVT(dst, format, ap); |
187 | 0 | } |
188 | | #endif |
189 | | |
190 | | } // namespace butil |