1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
|
// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#if !V8_ENABLE_WEBASSEMBLY
#error This header should only be included if WebAssembly is enabled.
#endif // !V8_ENABLE_WEBASSEMBLY
#ifndef V8_WASM_STRING_BUILDER_H_
#define V8_WASM_STRING_BUILDER_H_
#include <cstring>
#include <string>
#include <vector>
#include "src/common/globals.h"
namespace v8 {
namespace internal {
namespace wasm {
// Similar to std::ostringstream, but about 4x faster.
// This base class works best for small-ish strings (up to kChunkSize); for
// producing large amounts of text, you probably want a subclass like
// MultiLineStringBuilder.
class StringBuilder {
public:
StringBuilder() : on_growth_(kReplacePreviousChunk) {}
explicit StringBuilder(const StringBuilder&) = delete;
StringBuilder& operator=(const StringBuilder&) = delete;
~StringBuilder() {
for (char* chunk : chunks_) delete[] chunk;
if (on_growth_ == kReplacePreviousChunk && start_ != stack_buffer_) {
delete[] start_;
}
}
// Reserves space for {n} characters and returns a pointer to its beginning.
// Clients *must* write all {n} characters after calling this!
// Don't call this directly, use operator<< overloads instead.
char* allocate(size_t n) {
if (remaining_bytes_ < n) Grow(n);
char* result = cursor_;
cursor_ += n;
remaining_bytes_ -= n;
return result;
}
// Convenience wrappers.
void write(const byte* data, size_t n) {
char* ptr = allocate(n);
memcpy(ptr, data, n);
}
void write(const char* data, size_t n) {
char* ptr = allocate(n);
memcpy(ptr, data, n);
}
const char* start() const { return start_; }
const char* cursor() const { return cursor_; }
size_t length() const { return static_cast<size_t>(cursor_ - start_); }
void rewind_to_start() {
remaining_bytes_ += length();
cursor_ = start_;
}
protected:
enum OnGrowth : bool { kKeepOldChunks, kReplacePreviousChunk };
// Useful for subclasses that divide the text into ranges, e.g. lines.
explicit StringBuilder(OnGrowth on_growth) : on_growth_(on_growth) {}
void start_here() { start_ = cursor_; }
private:
void Grow(size_t requested) {
size_t used = length();
size_t required = used + requested;
size_t chunk_size;
if (on_growth_ == kKeepOldChunks) {
// Usually grow by kChunkSize, unless super-long lines need even more.
chunk_size = required < kChunkSize ? kChunkSize : required * 2;
} else {
// When we only have one chunk, always (at least) double its size
// when it grows, to minimize both wasted memory and growth effort.
chunk_size = required * 2;
}
char* new_chunk = new char[chunk_size];
memcpy(new_chunk, start_, used);
if (on_growth_ == kKeepOldChunks) {
chunks_.push_back(new_chunk);
} else if (start_ != stack_buffer_) {
delete[] start_;
}
start_ = new_chunk;
cursor_ = new_chunk + used;
remaining_bytes_ = chunk_size - used;
}
// Start small, to be cheap for the common case.
static constexpr size_t kStackSize = 256;
// If we have to grow, grow in big steps.
static constexpr size_t kChunkSize = 1024 * 1024;
char stack_buffer_[kStackSize];
std::vector<char*> chunks_; // A very simple Zone, essentially.
char* start_ = stack_buffer_;
char* cursor_ = stack_buffer_;
size_t remaining_bytes_ = kStackSize;
const OnGrowth on_growth_;
};
inline StringBuilder& operator<<(StringBuilder& sb, const char* str) {
size_t len = strlen(str);
char* ptr = sb.allocate(len);
memcpy(ptr, str, len);
return sb;
}
inline StringBuilder& operator<<(StringBuilder& sb, char c) {
*sb.allocate(1) = c;
return sb;
}
inline StringBuilder& operator<<(StringBuilder& sb, const std::string& s) {
sb.write(s.data(), s.length());
return sb;
}
inline StringBuilder& operator<<(StringBuilder& sb, uint32_t n) {
if (n == 0) {
*sb.allocate(1) = '0';
return sb;
}
static constexpr size_t kBufferSize = 10; // Just enough for a uint32.
char buffer[kBufferSize];
char* end = buffer + kBufferSize;
char* out = end;
while (n != 0) {
*(--out) = '0' + (n % 10);
n /= 10;
}
sb.write(out, static_cast<size_t>(end - out));
return sb;
}
inline StringBuilder& operator<<(StringBuilder& sb, int value) {
if (value >= 0) {
sb << static_cast<uint32_t>(value);
} else {
sb << "-" << ((~static_cast<uint32_t>(value)) + 1);
}
return sb;
}
} // namespace wasm
} // namespace internal
} // namespace v8
#endif // V8_WASM_STRING_BUILDER_H_
|