blob: cebaf13cde0f5751950c20d425382dc5221a6192 [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
#include <stdio.h> // vsnprintf
#include <string.h> // strlen
#include "butil/string_printf.h"
namespace butil {
// Copyright 2012 Facebook, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
namespace {
inline int string_printf_impl(std::string& output, const char* format,
va_list args) {
// Tru to the space at the end of output for our output buffer.
// Find out write point then inflate its size temporarily to its
// capacity; we will later shrink it to the size needed to represent
// the formatted string. If this buffer isn't large enough, we do a
// resize and try again.
const int write_point = output.size();
int remaining = output.capacity() - write_point;
output.resize(output.capacity());
va_list copied_args;
va_copy(copied_args, args);
int bytes_used = vsnprintf(&output[write_point], remaining, format,
copied_args);
va_end(copied_args);
if (bytes_used < 0) {
return -1;
} else if (bytes_used < remaining) {
// There was enough room, just shrink and return.
output.resize(write_point + bytes_used);
} else {
output.resize(write_point + bytes_used + 1);
remaining = bytes_used + 1;
bytes_used = vsnprintf(&output[write_point], remaining, format, args);
if (bytes_used + 1 != remaining) {
return -1;
}
output.resize(write_point + bytes_used);
}
return 0;
}
inline int string_printf_impl(std::string& output, size_t hint,
const char* format, va_list args) {
if (hint > output.capacity()) {
output.reserve(hint);
}
return string_printf_impl(output, format, args);
}
} // end anonymous namespace
std::string string_printf(const char* format, ...) {
// snprintf will tell us how large the output buffer should be, but
// we then have to call it a second time, which is costly. By
// guestimating the final size, we avoid the double snprintf in many
// cases, resulting in a performance win. We use this constructor
// of std::string to avoid a double allocation, though it does pad
// the resulting string with nul bytes. Our guestimation is twice
// the format string size, or 32 bytes, whichever is larger. This
// is a heuristic that doesn't affect correctness but attempts to be
// reasonably fast for the most common cases.
std::string ret;
va_list ap;
va_start(ap, format);
if (string_printf_impl(ret, std::max(32UL, strlen(format) * 2),
format, ap) != 0) {
ret.clear();
}
va_end(ap);
return ret;
}
std::string string_printf(size_t hint_size, const char* format, ...) {
// snprintf will tell us how large the output buffer should be, but
// we then have to call it a second time, which is costly. By
// passing the hint size of formatted string, we avoid the double
// snprintf in many cases, resulting in a performance win. We use
// this constructor of std::string to avoid a double allocation,
// though it does pad the resulting string with nul bytes.
std::string ret;
va_list ap;
va_start(ap, format);
if (string_printf_impl(ret, std::max(hint_size, strlen(format) * 2),
format, ap) != 0) {
ret.clear();
}
va_end(ap);
return ret;
}
// Basic declarations; allow for parameters of strings and string
// pieces to be specified.
int string_appendf(std::string* output, const char* format, ...) {
va_list ap;
va_start(ap, format);
const int rc = string_vappendf(output, format, ap);
va_end(ap);
return rc;
}
int string_vappendf(std::string* output, const char* format, va_list args) {
const size_t old_size = output->size();
const int rc = string_printf_impl(*output, format, args);
if (rc != 0) {
output->resize(old_size);
}
return rc;
}
int string_printf(std::string* output, const char* format, ...) {
va_list ap;
va_start(ap, format);
const int rc = string_vprintf(output, format, ap);
va_end(ap);
return rc;
};
int string_vprintf(std::string* output, const char* format, va_list args) {
output->clear();
const int rc = string_printf_impl(*output, format, args);
if (rc != 0) {
output->clear();
}
return rc;
};
} // namespace butil