| /* |
| * 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_ |