blob: 587b2f93276d46080c65508ffc86a0b219e11bf9 [file] [log] [blame]
/*
* Copyright (C) 2019 The Android Open Source Project
*
* 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.
*/
#ifndef INCLUDE_PERFETTO_EXT_BASE_STRING_WRITER_H_
#define INCLUDE_PERFETTO_EXT_BASE_STRING_WRITER_H_
#include <string.h>
#include <cinttypes>
#include <cmath>
#include <cstdlib>
#include <limits>
#include "perfetto/base/logging.h"
#include "perfetto/ext/base/string_utils.h"
#include "perfetto/ext/base/string_view.h"
namespace perfetto {
namespace base {
// A helper class which writes formatted data to a string buffer.
// This is used in the trace processor where we write O(GBs) of strings and
// sprintf is too slow.
class StringWriter {
public:
// Creates a string buffer from a char buffer and length.
StringWriter(char* buffer, size_t size) : buffer_(buffer), size_(size) {}
// Appends n instances of a char to the buffer.
void AppendChar(char in, size_t n = 1) {
PERFETTO_DCHECK(pos_ + n <= size_);
memset(&buffer_[pos_], in, n);
pos_ += n;
}
// Appends a length delimited string to the buffer.
void AppendString(const char* in, size_t n) {
PERFETTO_DCHECK(pos_ + n <= size_);
memcpy(&buffer_[pos_], in, n);
pos_ += n;
}
void AppendStringView(StringView sv) { AppendString(sv.data(), sv.size()); }
// Appends a null-terminated string literal to the buffer.
template <size_t N>
inline void AppendLiteral(const char (&in)[N]) {
AppendString(in, N - 1);
}
// Appends a StringView to the buffer.
void AppendString(StringView data) { AppendString(data.data(), data.size()); }
// Appends an integer to the buffer.
void AppendInt(int64_t value) { AppendPaddedInt<'0', 0>(value); }
// Appends an integer to the buffer, padding with |padchar| if the number of
// digits of the integer is less than |padding|.
template <char padchar, uint64_t padding>
void AppendPaddedInt(int64_t sign_value) {
const bool negate = std::signbit(static_cast<double>(sign_value));
uint64_t absolute_value;
if (sign_value == std::numeric_limits<int64_t>::min()) {
absolute_value =
static_cast<uint64_t>(std::numeric_limits<int64_t>::max()) + 1;
} else {
absolute_value = static_cast<uint64_t>(std::abs(sign_value));
}
AppendPaddedInt<padchar, padding>(absolute_value, negate);
}
void AppendUnsignedInt(uint64_t value) {
AppendPaddedUnsignedInt<'0', 0>(value);
}
// Appends an unsigned integer to the buffer, padding with |padchar| if the
// number of digits of the integer is less than |padding|.
template <char padchar, uint64_t padding>
void AppendPaddedUnsignedInt(uint64_t value) {
AppendPaddedInt<padchar, padding>(value, false);
}
// Appends a hex integer to the buffer.
template <typename IntType>
void AppendHexInt(IntType value) {
// TODO(lalitm): trying to optimize this is premature given we almost never
// print hex ints. Reevaluate this in the future if we do print them more.
size_t res =
base::SprintfTrunc(buffer_ + pos_, size_ - pos_, "%" PRIx64, value);
PERFETTO_DCHECK(pos_ + res <= size_);
pos_ += res;
}
// Appends a double to the buffer.
void AppendDouble(double value) {
// TODO(lalitm): trying to optimize this is premature given we almost never
// print doubles. Reevaluate this in the future if we do print them more.
size_t res = base::SprintfTrunc(buffer_ + pos_, size_ - pos_, "%lf", value);
PERFETTO_DCHECK(pos_ + res <= size_);
pos_ += res;
}
void AppendBool(bool value) {
if (value) {
AppendLiteral("true");
return;
}
AppendLiteral("false");
}
StringView GetStringView() {
PERFETTO_DCHECK(pos_ <= size_);
return StringView(buffer_, pos_);
}
char* CreateStringCopy() {
char* dup = reinterpret_cast<char*>(malloc(pos_ + 1));
if (dup) {
memcpy(dup, buffer_, pos_);
dup[pos_] = '\0';
}
return dup;
}
size_t pos() const { return pos_; }
size_t size() const { return size_; }
void reset() { pos_ = 0; }
private:
template <char padchar, uint64_t padding>
void AppendPaddedInt(uint64_t absolute_value, bool negate) {
// Need to add 2 to the number of digits to account for minus sign and
// rounding down of digits10.
constexpr auto kMaxDigits = std::numeric_limits<uint64_t>::digits10 + 2;
constexpr auto kSizeNeeded = kMaxDigits > padding ? kMaxDigits : padding;
PERFETTO_DCHECK(pos_ + kSizeNeeded <= size_);
char data[kSizeNeeded];
size_t idx;
for (idx = kSizeNeeded - 1; absolute_value >= 10;) {
char digit = absolute_value % 10;
absolute_value /= 10;
data[idx--] = digit + '0';
}
data[idx--] = static_cast<char>(absolute_value) + '0';
if (padding > 0) {
size_t num_digits = kSizeNeeded - 1 - idx;
// std::max() needed to work around GCC not being able to tell that
// padding > 0.
for (size_t i = num_digits; i < std::max(uint64_t{1u}, padding); i++) {
data[idx--] = padchar;
}
}
if (negate)
buffer_[pos_++] = '-';
AppendString(&data[idx + 1], kSizeNeeded - idx - 1);
}
char* buffer_ = nullptr;
size_t size_ = 0;
size_t pos_ = 0;
};
} // namespace base
} // namespace perfetto
#endif // INCLUDE_PERFETTO_EXT_BASE_STRING_WRITER_H_