Line data Source code
1 : // Copyright 2016 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_COLLECTOR_H_
6 : #define V8_COLLECTOR_H_
7 :
8 : #include <vector>
9 :
10 : #include "src/checks.h"
11 : #include "src/vector.h"
12 :
13 : namespace v8 {
14 : namespace internal {
15 :
16 : /*
17 : * A class that collects values into a backing store.
18 : * Specialized versions of the class can allow access to the backing store
19 : * in different ways.
20 : * There is no guarantee that the backing store is contiguous (and, as a
21 : * consequence, no guarantees that consecutively added elements are adjacent
22 : * in memory). The collector may move elements unless it has guaranteed not
23 : * to.
24 : */
25 : template <typename T, int growth_factor = 2, int max_growth = 1 * MB>
26 : class Collector {
27 : public:
28 : explicit Collector(int initial_capacity = kMinCapacity)
29 50 : : index_(0), size_(0) {
30 25 : current_chunk_ = Vector<T>::New(initial_capacity);
31 : }
32 :
33 25 : virtual ~Collector() {
34 : // Free backing store (in reverse allocation order).
35 : current_chunk_.Dispose();
36 180 : for (auto rit = chunks_.rbegin(); rit != chunks_.rend(); ++rit) {
37 : rit->Dispose();
38 : }
39 50 : }
40 :
41 : // Add a single element.
42 174905 : inline void Add(T value) {
43 349810 : if (index_ >= current_chunk_.length()) {
44 : Grow(1);
45 : }
46 349810 : current_chunk_[index_] = value;
47 174905 : index_++;
48 174905 : size_++;
49 174905 : }
50 :
51 : // Add a block of contiguous elements and return a Vector backed by the
52 : // memory area.
53 : // A basic Collector will keep this vector valid as long as the Collector
54 : // is alive.
55 2994 : inline Vector<T> AddBlock(int size, T initial_value) {
56 : DCHECK_GT(size, 0);
57 2994 : if (size > current_chunk_.length() - index_) {
58 65 : Grow(size);
59 : }
60 2994 : T* position = current_chunk_.start() + index_;
61 2994 : index_ += size;
62 2994 : size_ += size;
63 6078600 : for (int i = 0; i < size; i++) {
64 3037803 : position[i] = initial_value;
65 : }
66 2994 : return Vector<T>(position, size);
67 : }
68 :
69 : // Add a contiguous block of elements and return a vector backed
70 : // by the added block.
71 : // A basic Collector will keep this vector valid as long as the Collector
72 : // is alive.
73 5 : inline Vector<T> AddBlock(Vector<const T> source) {
74 5 : if (source.length() > current_chunk_.length() - index_) {
75 5 : Grow(source.length());
76 : }
77 5 : T* position = current_chunk_.start() + index_;
78 5 : index_ += source.length();
79 5 : size_ += source.length();
80 325 : for (int i = 0; i < source.length(); i++) {
81 320 : position[i] = source[i];
82 : }
83 5 : return Vector<T>(position, source.length());
84 : }
85 :
86 : // Write the contents of the collector into the provided vector.
87 15 : void WriteTo(Vector<T> destination) {
88 : DCHECK(size_ <= destination.length());
89 : int position = 0;
90 160 : for (const Vector<T>& chunk : chunks_) {
91 5567315 : for (int j = 0; j < chunk.length(); j++) {
92 8350755 : destination[position] = chunk[j];
93 2783585 : position++;
94 : }
95 : }
96 817291 : for (int i = 0; i < index_; i++) {
97 1225914 : destination[position] = current_chunk_[i];
98 408638 : position++;
99 : }
100 15 : }
101 :
102 : // Allocate a single contiguous vector, copy all the collected
103 : // elements to the vector, and return it.
104 : // The caller is responsible for freeing the memory of the returned
105 : // vector (e.g., using Vector::Dispose).
106 10 : Vector<T> ToVector() {
107 10 : Vector<T> new_store = Vector<T>::New(size_);
108 10 : WriteTo(new_store);
109 10 : return new_store;
110 : }
111 :
112 : // Resets the collector to be empty.
113 0 : virtual void Reset() {
114 0 : for (auto rit = chunks_.rbegin(); rit != chunks_.rend(); ++rit) {
115 : rit->Dispose();
116 : }
117 : chunks_.clear();
118 0 : index_ = 0;
119 0 : size_ = 0;
120 0 : }
121 :
122 : // Total number of elements added to collector so far.
123 : inline int size() { return size_; }
124 :
125 : protected:
126 : static const int kMinCapacity = 16;
127 : std::vector<Vector<T>> chunks_;
128 : Vector<T> current_chunk_; // Block of memory currently being written into.
129 : int index_; // Current index in current chunk.
130 : int size_; // Total number of elements in collector.
131 :
132 : // Creates a new current chunk, and stores the old chunk in the chunks_ list.
133 70 : void Grow(int min_capacity) {
134 : DCHECK_GT(growth_factor, 1);
135 : int new_capacity;
136 : int current_length = current_chunk_.length();
137 170 : if (current_length < kMinCapacity) {
138 : // The collector started out as empty.
139 0 : new_capacity = min_capacity * growth_factor;
140 0 : if (new_capacity < kMinCapacity) new_capacity = kMinCapacity;
141 : } else {
142 : int growth = current_length * (growth_factor - 1);
143 160 : if (growth > max_growth) {
144 : growth = max_growth;
145 : }
146 160 : new_capacity = current_length + growth;
147 160 : if (new_capacity < min_capacity) {
148 10 : new_capacity = min_capacity + growth;
149 : }
150 : }
151 170 : NewChunk(new_capacity);
152 : DCHECK(index_ + min_capacity <= current_chunk_.length());
153 70 : }
154 :
155 : // Before replacing the current chunk, give a subclass the option to move
156 : // some of the current data into the new chunk. The function may update
157 : // the current index_ value to represent data no longer in the current chunk.
158 : // Returns the initial index of the new chunk (after copied data).
159 110 : virtual void NewChunk(int new_capacity) {
160 : Vector<T> new_chunk = Vector<T>::New(new_capacity);
161 110 : if (index_ > 0) {
162 200 : chunks_.push_back(current_chunk_.SubVector(0, index_));
163 : } else {
164 : current_chunk_.Dispose();
165 : }
166 110 : current_chunk_ = new_chunk;
167 110 : index_ = 0;
168 110 : }
169 : };
170 :
171 : /*
172 : * A collector that allows sequences of values to be guaranteed to
173 : * stay consecutive.
174 : * If the backing store grows while a sequence is active, the current
175 : * sequence might be moved, but after the sequence is ended, it will
176 : * not move again.
177 : * NOTICE: Blocks allocated using Collector::AddBlock(int) can move
178 : * as well, if inside an active sequence where another element is added.
179 : */
180 : template <typename T, int growth_factor = 2, int max_growth = 1 * MB>
181 : class SequenceCollector : public Collector<T, growth_factor, max_growth> {
182 : public:
183 : explicit SequenceCollector(int initial_capacity)
184 : : Collector<T, growth_factor, max_growth>(initial_capacity),
185 10 : sequence_start_(kNoSequence) {}
186 :
187 10 : ~SequenceCollector() override = default;
188 :
189 : void StartSequence() {
190 : DCHECK_EQ(sequence_start_, kNoSequence);
191 25005 : sequence_start_ = this->index_;
192 : }
193 :
194 : Vector<T> EndSequence() {
195 : DCHECK_NE(sequence_start_, kNoSequence);
196 25005 : int sequence_start = sequence_start_;
197 25005 : sequence_start_ = kNoSequence;
198 25005 : if (sequence_start == this->index_) return Vector<T>();
199 23080 : return this->current_chunk_.SubVector(sequence_start, this->index_);
200 : }
201 :
202 : // Drops the currently added sequence, and all collected elements in it.
203 : void DropSequence() {
204 : DCHECK_NE(sequence_start_, kNoSequence);
205 : int sequence_length = this->index_ - sequence_start_;
206 : this->index_ = sequence_start_;
207 : this->size_ -= sequence_length;
208 : sequence_start_ = kNoSequence;
209 : }
210 :
211 0 : void Reset() override {
212 0 : sequence_start_ = kNoSequence;
213 0 : this->Collector<T, growth_factor, max_growth>::Reset();
214 0 : }
215 :
216 : private:
217 : static const int kNoSequence = -1;
218 : int sequence_start_;
219 :
220 : // Move the currently active sequence to the new chunk.
221 60 : void NewChunk(int new_capacity) override {
222 60 : if (sequence_start_ == kNoSequence) {
223 : // Fall back on default behavior if no sequence has been started.
224 0 : this->Collector<T, growth_factor, max_growth>::NewChunk(new_capacity);
225 : return;
226 : }
227 60 : int sequence_length = this->index_ - sequence_start_;
228 60 : Vector<T> new_chunk = Vector<T>::New(sequence_length + new_capacity);
229 : DCHECK(sequence_length < new_chunk.length());
230 420 : for (int i = 0; i < sequence_length; i++) {
231 540 : new_chunk[i] = this->current_chunk_[sequence_start_ + i];
232 : }
233 60 : if (sequence_start_ > 0) {
234 110 : this->chunks_.push_back(
235 : this->current_chunk_.SubVector(0, sequence_start_));
236 : } else {
237 : this->current_chunk_.Dispose();
238 : }
239 60 : this->current_chunk_ = new_chunk;
240 60 : this->index_ = sequence_length;
241 60 : sequence_start_ = 0;
242 : }
243 : };
244 :
245 : } // namespace internal
246 : } // namespace v8
247 :
248 : #endif // V8_COLLECTOR_H_
|