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