1 // Copyright 2016 The Fuchsia 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 <fbl/string_printf.h>
6
7 #include <stdarg.h>
8 #include <stddef.h>
9 #include <stdio.h>
10
11 #include <zircon/assert.h>
12 #include <fbl/unique_ptr.h>
13
14 namespace fbl {
15
StringPrintf(const char * format,...)16 String StringPrintf(const char* format, ...) {
17 va_list ap;
18 va_start(ap, format);
19 String rv = StringVPrintf(format, ap);
20 va_end(ap);
21 return rv;
22 }
23
StringVPrintf(const char * format,va_list ap)24 String StringVPrintf(const char* format, va_list ap) {
25 // Size of the small stack buffer to use first. This should be kept in sync
26 // with the numbers in StringPrintfTest.StringPrintf_Boundary.
27 constexpr size_t kStackBufferSize = 1024u;
28
29 // First, try with a small buffer on the stack.
30 char stack_buf[kStackBufferSize];
31 // Copy |ap| (which can only be used once), in case we need to retry.
32 va_list ap_copy;
33 va_copy(ap_copy, ap);
34 int result = vsnprintf(stack_buf, kStackBufferSize, format, ap_copy);
35 va_end(ap_copy);
36 if (result < 0) {
37 // As far as I can tell, we'd only get |EOVERFLOW| if the result is so large
38 // that it can't be represented by an |int| (in which case retrying would be
39 // futile), so Chromium's implementation is wrong.
40 return String();
41 }
42 // |result| should be the number of characters we need, not including the
43 // terminating null. However, |vsnprintf()| always null-terminates!
44 size_t output_size = static_cast<size_t>(result);
45 // Check if the output fit into our stack buffer. This is "<" not "<=", since
46 // |vsnprintf()| will null-terminate.
47 if (output_size < kStackBufferSize) {
48 // It fit.
49 return String(stack_buf, static_cast<size_t>(result));
50 }
51
52 // Since we have the required output size, we can just heap allocate that.
53 // (Add 1 because |vsnprintf()| will always null-terminate.)
54 size_t heap_buf_size = output_size + 1u;
55 fbl::unique_ptr<char[]> heap_buf(new char[heap_buf_size]);
56 result = vsnprintf(heap_buf.get(), heap_buf_size, format, ap);
57 ZX_ASSERT(result >= 0 && static_cast<size_t>(result) == output_size);
58 return String(heap_buf.get(), static_cast<size_t>(result));
59 }
60
61 } // namespace fbl
62