• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <string.h>
21 
22 #include <cinttypes>
23 #include <cmath>
24 #include <cstdlib>
25 #include <limits>
26 
27 #include "perfetto/base/logging.h"
28 #include "perfetto/ext/base/string_utils.h"
29 #include "perfetto/ext/base/string_view.h"
30 
31 namespace perfetto {
32 namespace base {
33 
34 // A helper class which writes formatted data to a string buffer.
35 // This is used in the trace processor where we write O(GBs) of strings and
36 // sprintf is too slow.
37 class StringWriter {
38  public:
39   // Creates a string buffer from a char buffer and length.
StringWriter(char * buffer,size_t size)40   StringWriter(char* buffer, size_t size) : buffer_(buffer), size_(size) {}
41 
42   // Appends n instances of a char to the buffer.
43   void AppendChar(char in, size_t n = 1) {
44     PERFETTO_DCHECK(pos_ + n <= size_);
45     memset(&buffer_[pos_], in, n);
46     pos_ += n;
47   }
48 
49   // Appends a length delimited string to the buffer.
AppendString(const char * in,size_t n)50   void AppendString(const char* in, size_t n) {
51     PERFETTO_DCHECK(pos_ + n <= size_);
52     memcpy(&buffer_[pos_], in, n);
53     pos_ += n;
54   }
55 
AppendStringView(StringView sv)56   void AppendStringView(StringView sv) { AppendString(sv.data(), sv.size()); }
57 
58   // Appends a null-terminated string literal to the buffer.
59   template <size_t N>
AppendLiteral(const char (& in)[N])60   inline void AppendLiteral(const char (&in)[N]) {
61     AppendString(in, N - 1);
62   }
63 
64   // Appends a StringView to the buffer.
AppendString(StringView data)65   void AppendString(StringView data) { AppendString(data.data(), data.size()); }
66 
67   // Appends an integer to the buffer.
AppendInt(int64_t value)68   void AppendInt(int64_t value) { AppendPaddedInt<'0', 0>(value); }
69 
70   // Appends an integer to the buffer, padding with |padchar| if the number of
71   // digits of the integer is less than |padding|.
72   template <char padchar, uint64_t padding>
AppendPaddedInt(int64_t sign_value)73   void AppendPaddedInt(int64_t sign_value) {
74     const bool negate = std::signbit(static_cast<double>(sign_value));
75     uint64_t absolute_value;
76     if (sign_value == std::numeric_limits<int64_t>::min()) {
77       absolute_value =
78           static_cast<uint64_t>(std::numeric_limits<int64_t>::max()) + 1;
79     } else {
80       absolute_value = static_cast<uint64_t>(std::abs(sign_value));
81     }
82     AppendPaddedInt<padchar, padding>(absolute_value, negate);
83   }
84 
AppendUnsignedInt(uint64_t value)85   void AppendUnsignedInt(uint64_t value) {
86     AppendPaddedUnsignedInt<'0', 0>(value);
87   }
88 
89   // Appends an unsigned integer to the buffer, padding with |padchar| if the
90   // number of digits of the integer is less than |padding|.
91   template <char padchar, uint64_t padding>
AppendPaddedUnsignedInt(uint64_t value)92   void AppendPaddedUnsignedInt(uint64_t value) {
93     AppendPaddedInt<padchar, padding>(value, false);
94   }
95 
96   // Appends a hex integer to the buffer.
97   template <typename IntType>
AppendHexInt(IntType value)98   void AppendHexInt(IntType value) {
99     // TODO(lalitm): trying to optimize this is premature given we almost never
100     // print hex ints. Reevaluate this in the future if we do print them more.
101     size_t res =
102         base::SprintfTrunc(buffer_ + pos_, size_ - pos_, "%" PRIx64, value);
103     PERFETTO_DCHECK(pos_ + res <= size_);
104     pos_ += res;
105   }
106 
107   // Appends a double to the buffer.
AppendDouble(double value)108   void AppendDouble(double value) {
109     // TODO(lalitm): trying to optimize this is premature given we almost never
110     // print doubles. Reevaluate this in the future if we do print them more.
111     size_t res = base::SprintfTrunc(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       memcpy(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