// Copyright (c) 2011 Baidu, Inc.
//
// string_printf_impl, string_printf, string_appendf were taken from
// https://github.com/facebook/folly/blob/master/folly/String.cpp
// with minor changes:
//   - coding style
//   - replace exceptions with return code

#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;
}
}  // 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 hueristic that doesn't affect correctness but attempts to be
    // reasonably fast for the most common cases.
    std::string ret;
    ret.reserve(std::max(32UL, strlen(format) * 2));

    va_list ap;
    va_start(ap, format);
    if (string_printf_impl(ret, 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 size_t old_size = output->size();
    const int rc = string_printf_impl(*output, format, ap);
    if (rc != 0) {
        output->resize(old_size);
    }
    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) {
        return 0;
    }
    output->resize(old_size);
    return rc;
}

int string_printf(std::string* output, const char* format, ...) {
    va_list ap;
    va_start(ap, format);
    output->clear();
    const int rc = string_printf_impl(*output, format, ap);
    if (rc != 0) {
        output->clear();
    }
    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) {
        return 0;
    }
    output->clear();
    return rc;
};


}  // namespace butil
