• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2013 The Chromium Authors
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 "base/strings/stringprintf.h"
6 
7 #include <errno.h>
8 #include <stddef.h>
9 
10 #include <vector>
11 
12 #include "base/logging.h"
13 #include "base/scoped_clear_last_error.h"
14 #include "base/strings/string_util.h"
15 #include "build/build_config.h"
16 
17 namespace base {
18 
StringPrintV(const char * format,va_list ap)19 std::string StringPrintV(const char* format, va_list ap) {
20   std::string result;
21   StringAppendV(&result, format, ap);
22   return result;
23 }
24 
StringAppendV(std::string * dst,const char * format,va_list ap)25 void StringAppendV(std::string* dst, const char* format, va_list ap) {
26   // First try with a small fixed size buffer.
27   // This buffer size should be kept in sync with StringUtilTest.GrowBoundary
28   // and StringUtilTest.StringPrintfBounds.
29   char stack_buf[1024];
30 
31   va_list ap_copy;
32   va_copy(ap_copy, ap);
33 
34   base::ScopedClearLastError last_error;
35   int result = vsnprintf(stack_buf, std::size(stack_buf), format, ap_copy);
36   va_end(ap_copy);
37 
38   if (result >= 0 && static_cast<size_t>(result) < std::size(stack_buf)) {
39     // It fit.
40     dst->append(stack_buf, static_cast<size_t>(result));
41     return;
42   }
43 
44   // Repeatedly increase buffer size until it fits.
45   size_t mem_length = std::size(stack_buf);
46   while (true) {
47     if (result < 0) {
48 #if BUILDFLAG(IS_WIN)
49       // On Windows, vsnprintf always returns the number of characters in a
50       // fully-formatted string, so if we reach this point, something else is
51       // wrong and no amount of buffer-doubling is going to fix it.
52       return;
53 #else
54       if (errno != 0 && errno != EOVERFLOW) {
55         return;
56       }
57       // Try doubling the buffer size.
58       mem_length *= 2;
59 #endif
60     } else {
61       // We need exactly "result + 1" characters.
62       mem_length = static_cast<size_t>(result) + 1;
63     }
64 
65     if (mem_length > 32 * 1024 * 1024) {
66       // That should be plenty, don't try anything larger.  This protects
67       // against huge allocations when using vsnprintf implementations that
68       // return -1 for reasons other than overflow without setting errno.
69       DLOG(WARNING) << "Unable to printf the requested string due to size.";
70       return;
71     }
72 
73     std::vector<char> mem_buf(mem_length);
74 
75     // NOTE: You can only use a va_list once.  Since we're in a while loop, we
76     // need to make a new copy each time so we don't use up the original.
77     va_copy(ap_copy, ap);
78     result = vsnprintf(&mem_buf[0], mem_length, format, ap_copy);
79     va_end(ap_copy);
80 
81     if ((result >= 0) && (static_cast<size_t>(result) < mem_length)) {
82       // It fit.
83       dst->append(&mem_buf[0], static_cast<size_t>(result));
84       return;
85     }
86   }
87 }
88 
89 }  // namespace base
90