1 | // Licensed to the .NET Foundation under one or more agreements. |
2 | // The .NET Foundation licenses this file to you under the MIT license. |
3 | // See the LICENSE file in the project root for more information. |
4 | |
5 | #pragma once |
6 | |
7 | // An array of T that expands automatically (and never shrinks) to accomodate |
8 | // any index access. Elements added as a result of automatic expansion are |
9 | // value-initialized (that is, they are assigned T()). |
10 | template <class T> |
11 | class JitExpandArray |
12 | { |
13 | protected: |
14 | CompAllocator m_alloc; // The allocator object that should be used to allocate members. |
15 | T* m_members; // Pointer to the element array. |
16 | unsigned m_size; // The size of the element array. |
17 | unsigned m_minSize; // The minimum size of the element array. |
18 | |
19 | // Ensure that the element array is large enough for the specified index to be valid. |
20 | void EnsureCoversInd(unsigned idx); |
21 | |
22 | //------------------------------------------------------------------------ |
23 | // InitializeRange: Value-initialize the specified range of elements. |
24 | // |
25 | // Arguments: |
26 | // low - inclusive lower bound of the range to initialize |
27 | // high - exclusive upper bound of the range to initialize |
28 | // |
29 | // Assumptions: |
30 | // Assumes that the element array has aready been allocated |
31 | // and that low and high are valid indices. The array is not |
32 | // expanded to accomodate invalid indices. |
33 | // |
34 | void InitializeRange(unsigned low, unsigned high) |
35 | { |
36 | assert(m_members != nullptr); |
37 | assert((low <= high) && (high <= m_size)); |
38 | for (unsigned i = low; i < high; i++) |
39 | { |
40 | m_members[i] = T(); |
41 | } |
42 | } |
43 | |
44 | public: |
45 | //------------------------------------------------------------------------ |
46 | // JitExpandArray: Construct an empty JitExpandArray object. |
47 | // |
48 | // Arguments: |
49 | // alloc - the allocator used to allocate the element array |
50 | // minSize - the initial size of the element array |
51 | // |
52 | // Notes: |
53 | // Initially no memory is allocated for the element array. The first |
54 | // time an array element (having index `idx`) is accessed, an array |
55 | // of size max(`minSize`, `idx`) is allocated. |
56 | // |
57 | JitExpandArray(CompAllocator alloc, unsigned minSize = 1) |
58 | : m_alloc(alloc), m_members(nullptr), m_size(0), m_minSize(minSize) |
59 | { |
60 | assert(minSize > 0); |
61 | } |
62 | |
63 | //------------------------------------------------------------------------ |
64 | // ~JitExpandArray: Destruct the JitExpandArray object. |
65 | // |
66 | // Notes: |
67 | // Frees the element array. Destructors of elements stored in the |
68 | // array are NOT invoked. |
69 | // |
70 | ~JitExpandArray() |
71 | { |
72 | if (m_members != nullptr) |
73 | { |
74 | m_alloc.deallocate(m_members); |
75 | } |
76 | } |
77 | |
78 | //------------------------------------------------------------------------ |
79 | // Init: Re-initialize the array to the empty state. |
80 | // |
81 | // Arguments: |
82 | // alloc - the allocator used to allocate the element array |
83 | // minSize - the initial size of the element array |
84 | // |
85 | // Notes: |
86 | // This is equivalent to calling the destructor and then constructing |
87 | // the array again. |
88 | // |
89 | void Init(CompAllocator alloc, unsigned minSize = 1) |
90 | { |
91 | if (m_members != nullptr) |
92 | { |
93 | m_alloc.deallocate(m_members); |
94 | } |
95 | m_alloc = alloc; |
96 | m_members = nullptr; |
97 | m_size = 0; |
98 | m_minSize = minSize; |
99 | } |
100 | |
101 | //------------------------------------------------------------------------ |
102 | // Reset: Change the minimum size and value-initialize all the elements. |
103 | // |
104 | // Arguments: |
105 | // minSize - the initial size of the element array |
106 | // |
107 | // Notes: |
108 | // Ensures that an element array of at least `minSize` elements |
109 | // has been allocated. |
110 | // |
111 | void Reset(unsigned minSize) |
112 | { |
113 | m_minSize = minSize; |
114 | Reset(); |
115 | } |
116 | |
117 | //------------------------------------------------------------------------ |
118 | // Reset: Value-initialize all the array elements. |
119 | // |
120 | // Notes: |
121 | // Ensures that an element array of at least `m_minSize` elements |
122 | // has been allocated. |
123 | // |
124 | void Reset() |
125 | { |
126 | if (m_minSize > m_size) |
127 | { |
128 | EnsureCoversInd(m_minSize - 1); |
129 | } |
130 | InitializeRange(0, m_size); |
131 | } |
132 | |
133 | //------------------------------------------------------------------------ |
134 | // Get: Get a copy of the element at index `idx`. |
135 | // |
136 | // Arguments: |
137 | // idx - the element index |
138 | // |
139 | // Return Value: |
140 | // A copy of the element at index `idx`. |
141 | // |
142 | // Notes: |
143 | // Expands the element array, if necessary, to contain `idx`. |
144 | // The result will be a value-initialized T if a value wasn't |
145 | // previously assigned to the specififed index. |
146 | // |
147 | T Get(unsigned idx) |
148 | { |
149 | EnsureCoversInd(idx); |
150 | return m_members[idx]; |
151 | } |
152 | |
153 | //------------------------------------------------------------------------ |
154 | // GetRef: Get a reference to the element at index `idx`. |
155 | // |
156 | // Arguments: |
157 | // idx - the element index |
158 | // |
159 | // Return Value: |
160 | // A reference to the element at index `idx`. |
161 | // |
162 | // Notes: |
163 | // Like `Get`, but returns a reference, so suitable for use as |
164 | // the LHS of an assignment. |
165 | // |
166 | T& GetRef(unsigned idx) |
167 | { |
168 | EnsureCoversInd(idx); |
169 | return m_members[idx]; |
170 | } |
171 | |
172 | //------------------------------------------------------------------------ |
173 | // Set: Assign a copy of `val` to the element at index `idx`. |
174 | // |
175 | // Arguments: |
176 | // idx - the element index |
177 | // val - the value to assign |
178 | // |
179 | // Notes: |
180 | // Expands the element array, if necessary, to contain `idx`. |
181 | // |
182 | void Set(unsigned idx, T val) |
183 | { |
184 | EnsureCoversInd(idx); |
185 | m_members[idx] = val; |
186 | } |
187 | |
188 | //------------------------------------------------------------------------ |
189 | // operator[]: Get a reference to the element at index `idx`. |
190 | // |
191 | // Arguments: |
192 | // idx - the element index |
193 | // |
194 | // Return Value: |
195 | // A reference to the element at index `idx`. |
196 | // |
197 | // Notes: |
198 | // Same as `GetRef`. |
199 | // |
200 | T& operator[](unsigned idx) |
201 | { |
202 | EnsureCoversInd(idx); |
203 | return m_members[idx]; |
204 | } |
205 | }; |
206 | |
207 | template <class T> |
208 | class JitExpandArrayStack : public JitExpandArray<T> |
209 | { |
210 | unsigned m_used; // The stack depth |
211 | |
212 | public: |
213 | //------------------------------------------------------------------------ |
214 | // JitExpandArrayStack: Construct an empty JitExpandArrayStack object. |
215 | // |
216 | // Arguments: |
217 | // alloc - the allocator used to allocate the element array |
218 | // minSize - the initial size of the element array |
219 | // |
220 | // Notes: |
221 | // See JitExpandArray constructor notes. |
222 | // |
223 | JitExpandArrayStack(CompAllocator alloc, unsigned minSize = 1) : JitExpandArray<T>(alloc, minSize), m_used(0) |
224 | { |
225 | } |
226 | |
227 | //------------------------------------------------------------------------ |
228 | // Set: Assign value a copy of `val` to the element at index `idx`. |
229 | // |
230 | // Arguments: |
231 | // idx - the index of element |
232 | // val - the value to assign |
233 | // |
234 | // Notes: |
235 | // Expands the element array, if necessary, to contain `idx`. |
236 | // If `idx` is larger than the current stack depth then this |
237 | // is the equivalent of series of Push(T()) followed by a Push(val). |
238 | // |
239 | void Set(unsigned idx, T val) |
240 | { |
241 | JitExpandArray<T>::Set(idx, val); |
242 | m_used = max((idx + 1), m_used); |
243 | } |
244 | |
245 | //------------------------------------------------------------------------ |
246 | // Reset: Remove all the elements from the stack. |
247 | // |
248 | void Reset() |
249 | { |
250 | JitExpandArray<T>::Reset(); |
251 | m_used = 0; |
252 | } |
253 | |
254 | //------------------------------------------------------------------------ |
255 | // Push: Push a copy of the specified value onto the stack. |
256 | // |
257 | // Arguments: |
258 | // val - the value |
259 | // |
260 | // Return Value: |
261 | // The index of the pushed value. |
262 | // |
263 | unsigned Push(T val) |
264 | { |
265 | unsigned res = m_used; |
266 | JitExpandArray<T>::Set(m_used, val); |
267 | m_used++; |
268 | return res; |
269 | } |
270 | |
271 | //------------------------------------------------------------------------ |
272 | // Pop: Remove the top element of the stack. |
273 | // |
274 | // Return Value: |
275 | // A copy of the removed element. |
276 | // |
277 | // Assumptions: |
278 | // The stack must not be empty. |
279 | // |
280 | T Pop() |
281 | { |
282 | assert(Size() > 0); |
283 | m_used--; |
284 | return this->m_members[m_used]; |
285 | } |
286 | |
287 | //------------------------------------------------------------------------ |
288 | // Top: Get a copy of the top element. |
289 | // |
290 | // Return Value: |
291 | // A copy of the top element. |
292 | // |
293 | // Assumptions: |
294 | // The stack must not be empty. |
295 | // |
296 | T Top() |
297 | { |
298 | assert(Size() > 0); |
299 | return this->m_members[m_used - 1]; |
300 | } |
301 | |
302 | //------------------------------------------------------------------------ |
303 | // TopRef: Get a reference to the top element. |
304 | // |
305 | // Return Value: |
306 | // A reference to the top element. |
307 | // |
308 | // Assumptions: |
309 | // The stack must not be empty. |
310 | // |
311 | T& TopRef() |
312 | { |
313 | assert(Size() > 0); |
314 | return this->m_members[m_used - 1]; |
315 | } |
316 | |
317 | //------------------------------------------------------------------------ |
318 | // GetNoExpand: Get a copy of the element at index `idx`. |
319 | // |
320 | // Arguments: |
321 | // idx - the element index |
322 | // |
323 | // Return Value: |
324 | // A copy of the element at index `idx`. |
325 | // |
326 | // Notes: |
327 | // Unlike `Get` this does not expand the array if the index is not valid. |
328 | // |
329 | // Assumptions: |
330 | // The element index does not exceed the current stack depth. |
331 | // |
332 | T GetNoExpand(unsigned idx) |
333 | { |
334 | assert(idx < m_used); |
335 | return this->m_members[idx]; |
336 | } |
337 | |
338 | //------------------------------------------------------------------------ |
339 | // Remove: Remove the element at index `idx`. |
340 | // |
341 | // Arguments: |
342 | // idx - the element index |
343 | // |
344 | // Notes: |
345 | // Shifts contents of the array beyond `idx`, if any, to occupy the free |
346 | // slot created at `idx`. O(n) worst case operation, no memory is allocated. |
347 | // Elements are bitwise copied, copy constructors are NOT invoked. |
348 | // |
349 | // Assumptions: |
350 | // The element index does not exceed the current stack depth. |
351 | // |
352 | void Remove(unsigned idx) |
353 | { |
354 | assert(idx < m_used); |
355 | if (idx < m_used - 1) |
356 | { |
357 | memmove(&this->m_members[idx], &this->m_members[idx + 1], (m_used - idx - 1) * sizeof(T)); |
358 | } |
359 | m_used--; |
360 | } |
361 | |
362 | //------------------------------------------------------------------------ |
363 | // Size: Get the current stack depth. |
364 | // |
365 | // Return Value: |
366 | // The stack depth. |
367 | // |
368 | unsigned Size() |
369 | { |
370 | return m_used; |
371 | } |
372 | }; |
373 | |
374 | //------------------------------------------------------------------------ |
375 | // EnsureCoversInd: Ensure that the array is large enough for the specified |
376 | // index to be valid. |
377 | // |
378 | // Arguments: |
379 | // idx - the element index |
380 | // |
381 | // Notes: |
382 | // If the array is expanded then |
383 | // - the existing elements are bitwise copied (copy constructors are NOT invoked) |
384 | // - the newly added elements are value-initialized |
385 | // |
386 | template <class T> |
387 | void JitExpandArray<T>::EnsureCoversInd(unsigned idx) |
388 | { |
389 | if (idx >= m_size) |
390 | { |
391 | unsigned oldSize = m_size; |
392 | T* oldMembers = m_members; |
393 | m_size = max(idx + 1, max(m_minSize, m_size * 2)); |
394 | m_members = m_alloc.allocate<T>(m_size); |
395 | if (oldMembers != nullptr) |
396 | { |
397 | memcpy(m_members, oldMembers, oldSize * sizeof(T)); |
398 | m_alloc.deallocate(oldMembers); |
399 | } |
400 | InitializeRange(oldSize, m_size); |
401 | } |
402 | } |
403 | |