1// Copyright 2014 The Android Open Source Project
2//
3// This software is licensed under the terms of the GNU General Public
4// License version 2, as published by the Free Software Foundation, and
5// may be copied, distributed, and modified under those terms.
6//
7// This program is distributed in the hope that it will be useful,
8// but WITHOUT ANY WARRANTY; without even the implied warranty of
9// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10// GNU General Public License for more details.
11
12#include "android/base/StringFormat.h"
13
14#include <stdio.h>
15
16namespace android {
17namespace base {
18
19String StringFormat(const char* format, ...) {
20    va_list args;
21    va_start(args, format);
22    String result = StringFormatWithArgs(format, args);
23    va_end(args);
24    return result;
25}
26
27String StringFormatWithArgs(const char* format, va_list args) {
28    String result;
29    StringAppendFormatWithArgs(&result, format, args);
30    return result;
31}
32
33void StringAppendFormat(String* string, const char* format, ...) {
34    va_list args;
35    va_start(args, format);
36    StringAppendFormatWithArgs(string, format, args);
37    va_end(args);
38}
39
40void StringAppendFormatWithArgs(String* string,
41                                const char* format,
42                                va_list args) {
43    size_t cur_size = string->size();
44    size_t extra = 0;
45    for (;;) {
46        va_list args2;
47        va_copy(args2, args);
48        int ret = vsnprintf(&(*string)[cur_size], extra, format, args2);
49        va_end(args2);
50
51        if (ret == 0) {
52            // Nothing to do here.
53            break;
54        }
55
56        if (ret > 0) {
57            size_t ret_sz = static_cast<size_t>(ret);
58            if (extra == 0) {
59                // First pass, resize the string and try again.
60                extra = ret_sz + 1;
61                string->resize(cur_size + extra);
62                continue;
63            }
64            if (ret_sz < extra) {
65                // Second pass or later, success!
66                string->resize(cur_size + ret_sz);
67                return;
68            }
69        }
70
71        // NOTE: The MSVCRT.DLL implementation of snprintf() is broken and
72        // will return -1 in case of truncation. This code path is taken
73        // when this happens, or when |ret_sz| is equal or larger than
74        // |extra|. Grow the buffer to allow for more room, then try again.
75        extra += (extra >> 1) + 32;
76        string->resize(cur_size + extra);
77    }
78}
79
80
81}  // namespace base
82}  // namespace android
83