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 14namespace fbl { 15 16String 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 24String 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