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       : index_(0), size_(0) {
30     current_chunk_ = Vector<T>::New(initial_capacity);
31   }
32 
~Collector()33   virtual ~Collector() {
34     // Free backing store (in reverse allocation order).
35     current_chunk_.Dispose();
36     for (auto rit = chunks_.rbegin(); rit != chunks_.rend(); ++rit) {
37       rit->Dispose();
38     }
39   }
40 
41   // Add a single element.
Add(T value)42   inline void Add(T value) {
43     if (index_ >= current_chunk_.length()) {
44       Grow(1);
45     }
46     current_chunk_[index_] = value;
47     index_++;
48     size_++;
49   }
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.
AddBlock(int size,T initial_value)55   inline Vector<T> AddBlock(int size, T initial_value) {
56     DCHECK_GT(size, 0);
57     if (size > current_chunk_.length() - index_) {
58       Grow(size);
59     }
60     T* position = current_chunk_.start() + index_;
61     index_ += size;
62     size_ += size;
63     for (int i = 0; i < size; i++) {
64       position[i] = initial_value;
65     }
66     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.
AddBlock(Vector<const T> source)73   inline Vector<T> AddBlock(Vector<const T> source) {
74     if (source.length() > current_chunk_.length() - index_) {
75       Grow(source.length());
76     }
77     T* position = current_chunk_.start() + index_;
78     index_ += source.length();
79     size_ += source.length();
80     for (int i = 0; i < source.length(); i++) {
81       position[i] = source[i];
82     }
83     return Vector<T>(position, source.length());
84   }
85 
86   // Write the contents of the collector into the provided vector.
WriteTo(Vector<T> destination)87   void WriteTo(Vector<T> destination) {
88     DCHECK(size_ <= destination.length());
89     int position = 0;
90     for (const Vector<T>& chunk : chunks_) {
91       for (int j = 0; j < chunk.length(); j++) {
92         destination[position] = chunk[j];
93         position++;
94       }
95     }
96     for (int i = 0; i < index_; i++) {
97       destination[position] = current_chunk_[i];
98       position++;
99     }
100   }
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).
ToVector()106   Vector<T> ToVector() {
107     Vector<T> new_store = Vector<T>::New(size_);
108     WriteTo(new_store);
109     return new_store;
110   }
111 
112   // Resets the collector to be empty.
Reset()113   virtual void Reset() {
114     for (auto rit = chunks_.rbegin(); rit != chunks_.rend(); ++rit) {
115       rit->Dispose();
116     }
117     chunks_.clear();
118     index_ = 0;
119     size_ = 0;
120   }
121 
122   // Total number of elements added to collector so far.
size()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.
Grow(int min_capacity)133   void Grow(int min_capacity) {
134     DCHECK_GT(growth_factor, 1);
135     int new_capacity;
136     int current_length = current_chunk_.length();
137     if (current_length < kMinCapacity) {
138       // The collector started out as empty.
139       new_capacity = min_capacity * growth_factor;
140       if (new_capacity < kMinCapacity) new_capacity = kMinCapacity;
141     } else {
142       int growth = current_length * (growth_factor - 1);
143       if (growth > max_growth) {
144         growth = max_growth;
145       }
146       new_capacity = current_length + growth;
147       if (new_capacity < min_capacity) {
148         new_capacity = min_capacity + growth;
149       }
150     }
151     NewChunk(new_capacity);
152     DCHECK(index_ + min_capacity <= current_chunk_.length());
153   }
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).
NewChunk(int new_capacity)159   virtual void NewChunk(int new_capacity) {
160     Vector<T> new_chunk = Vector<T>::New(new_capacity);
161     if (index_ > 0) {
162       chunks_.push_back(current_chunk_.SubVector(0, index_));
163     } else {
164       current_chunk_.Dispose();
165     }
166     current_chunk_ = new_chunk;
167     index_ = 0;
168   }
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:
SequenceCollector(int initial_capacity)183   explicit SequenceCollector(int initial_capacity)
184       : Collector<T, growth_factor, max_growth>(initial_capacity),
185         sequence_start_(kNoSequence) {}
186 
~SequenceCollector()187   virtual ~SequenceCollector() {}
188 
StartSequence()189   void StartSequence() {
190     DCHECK_EQ(sequence_start_, kNoSequence);
191     sequence_start_ = this->index_;
192   }
193 
EndSequence()194   Vector<T> EndSequence() {
195     DCHECK_NE(sequence_start_, kNoSequence);
196     int sequence_start = sequence_start_;
197     sequence_start_ = kNoSequence;
198     if (sequence_start == this->index_) return Vector<T>();
199     return this->current_chunk_.SubVector(sequence_start, this->index_);
200   }
201 
202   // Drops the currently added sequence, and all collected elements in it.
DropSequence()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 
Reset()211   virtual void Reset() {
212     sequence_start_ = kNoSequence;
213     this->Collector<T, growth_factor, max_growth>::Reset();
214   }
215 
216  private:
217   static const int kNoSequence = -1;
218   int sequence_start_;
219 
220   // Move the currently active sequence to the new chunk.
NewChunk(int new_capacity)221   virtual void NewChunk(int new_capacity) {
222     if (sequence_start_ == kNoSequence) {
223       // Fall back on default behavior if no sequence has been started.
224       this->Collector<T, growth_factor, max_growth>::NewChunk(new_capacity);
225       return;
226     }
227     int sequence_length = this->index_ - sequence_start_;
228     Vector<T> new_chunk = Vector<T>::New(sequence_length + new_capacity);
229     DCHECK(sequence_length < new_chunk.length());
230     for (int i = 0; i < sequence_length; i++) {
231       new_chunk[i] = this->current_chunk_[sequence_start_ + i];
232     }
233     if (sequence_start_ > 0) {
234       this->chunks_.push_back(
235           this->current_chunk_.SubVector(0, sequence_start_));
236     } else {
237       this->current_chunk_.Dispose();
238     }
239     this->current_chunk_ = new_chunk;
240     this->index_ = sequence_length;
241     sequence_start_ = 0;
242   }
243 };
244 
245 }  // namespace internal
246 }  // namespace v8
247 
248 #endif  // V8_COLLECTOR_H_
249