1 // Copyright 2022 the V8 project authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef V8_PROFILER_OUTPUT_STREAM_WRITER_H_ 6 #define V8_PROFILER_OUTPUT_STREAM_WRITER_H_ 7 8 #include <algorithm> 9 #include <string> 10 11 #include "include/v8-profiler.h" 12 #include "src/base/logging.h" 13 #include "src/base/strings.h" 14 #include "src/base/vector.h" 15 #include "src/common/globals.h" 16 #include "src/utils/memcopy.h" 17 18 namespace v8 { 19 namespace internal { 20 21 template <int bytes> 22 struct MaxDecimalDigitsIn; 23 template <> 24 struct MaxDecimalDigitsIn<1> { 25 static const int kSigned = 3; 26 static const int kUnsigned = 3; 27 }; 28 template <> 29 struct MaxDecimalDigitsIn<4> { 30 static const int kSigned = 11; 31 static const int kUnsigned = 10; 32 }; 33 template <> 34 struct MaxDecimalDigitsIn<8> { 35 static const int kSigned = 20; 36 static const int kUnsigned = 20; 37 }; 38 39 class OutputStreamWriter { 40 public: 41 explicit OutputStreamWriter(v8::OutputStream* stream) 42 : stream_(stream), 43 chunk_size_(stream->GetChunkSize()), 44 chunk_(chunk_size_), 45 chunk_pos_(0), 46 aborted_(false) { 47 DCHECK_GT(chunk_size_, 0); 48 } 49 bool aborted() { return aborted_; } 50 void AddCharacter(char c) { 51 DCHECK_NE(c, '\0'); 52 DCHECK(chunk_pos_ < chunk_size_); 53 chunk_[chunk_pos_++] = c; 54 MaybeWriteChunk(); 55 } 56 void AddString(const char* s) { 57 size_t len = strlen(s); 58 DCHECK_GE(kMaxInt, len); 59 AddSubstring(s, static_cast<int>(len)); 60 } 61 void AddSubstring(const char* s, int n) { 62 if (n <= 0) return; 63 DCHECK_LE(n, strlen(s)); 64 const char* s_end = s + n; 65 while (s < s_end) { 66 int s_chunk_size = 67 std::min(chunk_size_ - chunk_pos_, static_cast<int>(s_end - s)); 68 DCHECK_GT(s_chunk_size, 0); 69 MemCopy(chunk_.begin() + chunk_pos_, s, s_chunk_size); 70 s += s_chunk_size; 71 chunk_pos_ += s_chunk_size; 72 MaybeWriteChunk(); 73 } 74 } 75 void AddNumber(unsigned n) { AddNumberImpl<unsigned>(n, "%u"); } 76 void Finalize() { 77 if (aborted_) return; 78 DCHECK(chunk_pos_ < chunk_size_); 79 if (chunk_pos_ != 0) { 80 WriteChunk(); 81 } 82 stream_->EndOfStream(); 83 } 84 85 private: 86 template <typename T> 87 void AddNumberImpl(T n, const char* format) { 88 // Buffer for the longest value plus trailing \0 89 static const int kMaxNumberSize = 90 MaxDecimalDigitsIn<sizeof(T)>::kUnsigned + 1; 91 if (chunk_size_ - chunk_pos_ >= kMaxNumberSize) { 92 int result = 93 SNPrintF(chunk_.SubVector(chunk_pos_, chunk_size_), format, n); 94 DCHECK_NE(result, -1); 95 chunk_pos_ += result; 96 MaybeWriteChunk(); 97 } else { 98 base::EmbeddedVector<char, kMaxNumberSize> buffer; 99 int result = SNPrintF(buffer, format, n); 100 USE(result); 101 DCHECK_NE(result, -1); 102 AddString(buffer.begin()); 103 } 104 } 105 void MaybeWriteChunk() { 106 DCHECK(chunk_pos_ <= chunk_size_); 107 if (chunk_pos_ == chunk_size_) { 108 WriteChunk(); 109 } 110 } 111 void WriteChunk() { 112 if (aborted_) return; 113 if (stream_->WriteAsciiChunk(chunk_.begin(), chunk_pos_) == 114 v8::OutputStream::kAbort) 115 aborted_ = true; 116 chunk_pos_ = 0; 117 } 118 119 v8::OutputStream* stream_; 120 int chunk_size_; 121 base::ScopedVector<char> chunk_; 122 int chunk_pos_; 123 bool aborted_; 124 }; 125 126 } // namespace internal 127 } // namespace v8 128 129 #endif // V8_PROFILER_OUTPUT_STREAM_WRITER_H_ 130