1 /* 2 * Copyright (C) 2019 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #ifndef INCLUDE_PERFETTO_EXT_BASE_STRING_WRITER_H_ 18 #define INCLUDE_PERFETTO_EXT_BASE_STRING_WRITER_H_ 19 20 #include <inttypes.h> 21 #include <math.h> 22 #include <string.h> 23 #include <cmath> 24 #include <cstdlib> 25 #include <limits> 26 27 #include "perfetto/base/logging.h" 28 #include "perfetto/ext/base/string_view.h" 29 30 namespace perfetto { 31 namespace base { 32 33 // A helper class which writes formatted data to a string buffer. 34 // This is used in the trace processor where we write O(GBs) of strings and 35 // sprintf is too slow. 36 class StringWriter { 37 public: 38 // Creates a string buffer from a char buffer and length. StringWriter(char * buffer,size_t size)39 StringWriter(char* buffer, size_t size) : buffer_(buffer), size_(size) {} 40 41 // Appends n instances of a char to the buffer. 42 void AppendChar(char in, size_t n = 1) { 43 PERFETTO_DCHECK(pos_ + n <= size_); 44 memset(&buffer_[pos_], in, n); 45 pos_ += n; 46 } 47 48 // Appends a length delimited string to the buffer. AppendString(const char * in,size_t n)49 void AppendString(const char* in, size_t n) { 50 PERFETTO_DCHECK(pos_ + n <= size_); 51 memcpy(&buffer_[pos_], in, n); 52 pos_ += n; 53 } 54 AppendStringView(StringView sv)55 void AppendStringView(StringView sv) { AppendString(sv.data(), sv.size()); } 56 57 // Appends a null-terminated string literal to the buffer. 58 template <size_t N> AppendLiteral(const char (& in)[N])59 inline void AppendLiteral(const char (&in)[N]) { 60 AppendString(in, N - 1); 61 } 62 63 // Appends a StringView to the buffer. AppendString(StringView data)64 void AppendString(StringView data) { AppendString(data.data(), data.size()); } 65 66 // Appends an integer to the buffer. AppendInt(int64_t value)67 void AppendInt(int64_t value) { AppendPaddedInt<'0', 0>(value); } 68 69 // Appends an integer to the buffer, padding with |padchar| if the number of 70 // digits of the integer is less than |padding|. 71 template <char padchar, uint64_t padding> AppendPaddedInt(int64_t sign_value)72 void AppendPaddedInt(int64_t sign_value) { 73 const bool negate = std::signbit(static_cast<double>(sign_value)); 74 uint64_t absolute_value; 75 if (sign_value == std::numeric_limits<int64_t>::min()) { 76 absolute_value = 77 static_cast<uint64_t>(std::numeric_limits<int64_t>::max()) + 1; 78 } else { 79 absolute_value = static_cast<uint64_t>(std::abs(sign_value)); 80 } 81 AppendPaddedInt<padchar, padding>(absolute_value, negate); 82 } 83 AppendUnsignedInt(uint64_t value)84 void AppendUnsignedInt(uint64_t value) { 85 AppendPaddedUnsignedInt<'0', 0>(value); 86 } 87 88 // Appends an unsigned integer to the buffer, padding with |padchar| if the 89 // number of digits of the integer is less than |padding|. 90 template <char padchar, uint64_t padding> AppendPaddedUnsignedInt(uint64_t value)91 void AppendPaddedUnsignedInt(uint64_t value) { 92 AppendPaddedInt<padchar, padding>(value, false); 93 } 94 95 // Appends a hex integer to the buffer. 96 template <typename IntType> AppendHexInt(IntType value)97 void AppendHexInt(IntType value) { 98 // TODO(lalitm): trying to optimize this is premature given we almost never 99 // print hex ints. Reevaluate this in the future if we do print them more. 100 size_t res = static_cast<size_t>( 101 snprintf(buffer_ + pos_, size_ - pos_, "%" PRIx64, value)); 102 PERFETTO_DCHECK(pos_ + res <= size_); 103 pos_ += res; 104 } 105 106 // Appends a double to the buffer. AppendDouble(double value)107 void AppendDouble(double value) { 108 // TODO(lalitm): trying to optimize this is premature given we almost never 109 // print doubles. Reevaluate this in the future if we do print them more. 110 size_t res = static_cast<size_t>( 111 snprintf(buffer_ + pos_, size_ - pos_, "%lf", value)); 112 PERFETTO_DCHECK(pos_ + res <= size_); 113 pos_ += res; 114 } 115 AppendBool(bool value)116 void AppendBool(bool value) { 117 if (value) { 118 AppendLiteral("true"); 119 return; 120 } 121 AppendLiteral("false"); 122 } 123 GetStringView()124 StringView GetStringView() { 125 PERFETTO_DCHECK(pos_ <= size_); 126 return StringView(buffer_, pos_); 127 } 128 CreateStringCopy()129 char* CreateStringCopy() { 130 char* dup = reinterpret_cast<char*>(malloc(pos_ + 1)); 131 if (dup) { 132 strncpy(dup, buffer_, pos_); 133 dup[pos_] = '\0'; 134 } 135 return dup; 136 } 137 pos()138 size_t pos() const { return pos_; } size()139 size_t size() const { return size_; } reset()140 void reset() { pos_ = 0; } 141 142 private: 143 template <char padchar, uint64_t padding> AppendPaddedInt(uint64_t absolute_value,bool negate)144 void AppendPaddedInt(uint64_t absolute_value, bool negate) { 145 // Need to add 2 to the number of digits to account for minus sign and 146 // rounding down of digits10. 147 constexpr auto kMaxDigits = std::numeric_limits<uint64_t>::digits10 + 2; 148 constexpr auto kSizeNeeded = kMaxDigits > padding ? kMaxDigits : padding; 149 PERFETTO_DCHECK(pos_ + kSizeNeeded <= size_); 150 151 char data[kSizeNeeded]; 152 153 size_t idx; 154 for (idx = kSizeNeeded - 1; absolute_value >= 10;) { 155 char digit = absolute_value % 10; 156 absolute_value /= 10; 157 data[idx--] = digit + '0'; 158 } 159 data[idx--] = static_cast<char>(absolute_value) + '0'; 160 161 if (padding > 0) { 162 size_t num_digits = kSizeNeeded - 1 - idx; 163 // std::max() needed to work around GCC not being able to tell that 164 // padding > 0. 165 for (size_t i = num_digits; i < std::max(uint64_t{1u}, padding); i++) { 166 data[idx--] = padchar; 167 } 168 } 169 170 if (negate) 171 buffer_[pos_++] = '-'; 172 AppendString(&data[idx + 1], kSizeNeeded - idx - 1); 173 } 174 175 char* buffer_ = nullptr; 176 size_t size_ = 0; 177 size_t pos_ = 0; 178 }; 179 180 } // namespace base 181 } // namespace perfetto 182 183 #endif // INCLUDE_PERFETTO_EXT_BASE_STRING_WRITER_H_ 184