blob: 161d72cb227b57fb472211ef9389f454dc033d07 [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "tools/kazoo/string_util.h"
#include <errno.h>
#include <stdarg.h>
#include <zircon/assert.h>
#include <memory>
namespace {
void StringVAppendfHelper(std::string* dest, const char* format, va_list ap) {
// Size of the small stack buffer to use first. This should be kept in sync
// with the numbers in StringPrintfTest.StringPrintf_Boundary.
constexpr size_t kStackBufferSize = 1024u;
// First, try with a small buffer on the stack.
char stack_buf[kStackBufferSize];
// Copy |ap| (which can only be used once), in case we need to retry.
va_list ap_copy;
va_copy(ap_copy, ap);
int result = vsnprintf(stack_buf, kStackBufferSize, format, ap_copy);
va_end(ap_copy);
if (result < 0) {
// As far as I can tell, we'd only get |EOVERFLOW| if the result is so large
// that it can't be represented by an |int| (in which case retrying would be
// futile), so Chromium's implementation is wrong.
ZX_ASSERT(false);
return;
}
// |result| should be the number of characters we need, not including the
// terminating null. However, |vsnprintf()| always null-terminates!
size_t output_size = static_cast<size_t>(result);
// Check if the output fit into our stack buffer. This is "<" not "<=", since
// |vsnprintf()| will null-terminate.
if (output_size < kStackBufferSize) {
// It fit.
dest->append(stack_buf, static_cast<size_t>(result));
return;
}
// Since we have the required output size, we can just heap allocate that.
// (Add 1 because |vsnprintf()| will always null-terminate.)
size_t heap_buf_size = output_size + 1u;
std::unique_ptr<char[]> heap_buf(new char[heap_buf_size]);
result = vsnprintf(heap_buf.get(), heap_buf_size, format, ap);
if (result < 0 || static_cast<size_t>(result) > output_size) {
ZX_ASSERT(false);
return;
}
ZX_ASSERT(static_cast<size_t>(result) == output_size);
dest->append(heap_buf.get(), static_cast<size_t>(result));
}
} // namespace
bool ReadFileToString(const std::string& path, std::string* result) {
FILE* fp = fopen(path.c_str(), "rb");
if (!fp) {
return false;
}
fseek(fp, 0, SEEK_END);
result->resize(ftell(fp));
rewind(fp);
fread(&(*result)[0], 1, result->size(), fp);
fclose(fp);
return true;
}
std::string StringPrintf(const char* format, ...) {
va_list ap;
va_start(ap, format);
std::string rv;
StringVAppendfHelper(&rv, format, ap);
va_end(ap);
return rv;
}
std::string StringVPrintf(const char* format, va_list ap) {
std::string rv;
StringVAppendfHelper(&rv, format, ap);
return rv;
}
std::string TrimString(const std::string& str, const std::string& chars_to_trim) {
size_t start_index = str.find_first_not_of(chars_to_trim);
if (start_index == std::string::npos) {
return std::string();
}
size_t end_index = str.find_last_not_of(chars_to_trim);
return str.substr(start_index, end_index - start_index + 1);
}
std::vector<std::string> SplitString(const std::string& input, char delimiter,
WhitespaceHandling whitespace) {
std::vector<std::string> result;
auto start = input.begin();
for (auto end = start; end != input.end(); start = end + 1) {
end = start;
while (end != input.end() && *end != delimiter) {
++end;
}
if (whitespace == kKeepWhitespace) {
result.push_back(std::string(start, end));
} else {
result.push_back(TrimString(std::string(start, end), " \t\r\n"));
}
}
return result;
}
int64_t StringToInt(const std::string& str) {
return static_cast<int64_t>(strtoll(str.c_str(), nullptr, 10));
}
uint64_t StringToUInt(const std::string& str) {
return static_cast<uint64_t>(strtoull(str.c_str(), nullptr, 10));
}
bool StartsWith(const std::string& str, const std::string& prefix) {
return str.compare(0, prefix.size(), prefix) == 0;
}