1 | /** |
---|
2 | @file Array.h |
---|
3 | |
---|
4 | @maintainer Morgan McGuire, graphics3d.com |
---|
5 | @cite Portions written by Aaron Orenstein, a@orenstein.name |
---|
6 | |
---|
7 | @created 2001-03-11 |
---|
8 | @edited 2007-05-12 |
---|
9 | |
---|
10 | Copyright 2000-2007, Morgan McGuire. |
---|
11 | All rights reserved. |
---|
12 | */ |
---|
13 | |
---|
14 | #ifndef G3D_ARRAY_H |
---|
15 | #define G3D_ARRAY_H |
---|
16 | |
---|
17 | #include "G3D/platform.h" |
---|
18 | #include "G3D/debug.h" |
---|
19 | #include "G3D/System.h" |
---|
20 | #ifdef G3D_DEBUG |
---|
21 | // For formatting error messages |
---|
22 | # include "G3D/format.h" |
---|
23 | #endif |
---|
24 | #include <vector> |
---|
25 | #include <algorithm> |
---|
26 | |
---|
27 | #ifdef G3D_WIN32 |
---|
28 | # include <new> |
---|
29 | |
---|
30 | # pragma warning (push) |
---|
31 | // debug information too long |
---|
32 | # pragma warning( disable : 4312) |
---|
33 | # pragma warning( disable : 4786) |
---|
34 | #endif |
---|
35 | |
---|
36 | |
---|
37 | namespace G3D { |
---|
38 | |
---|
39 | /** |
---|
40 | Constant for passing to Array::resize |
---|
41 | */ |
---|
42 | const bool DONT_SHRINK_UNDERLYING_ARRAY = false; |
---|
43 | |
---|
44 | /** Constant for Array::sort */ |
---|
45 | const int SORT_INCREASING = 1; |
---|
46 | /** Constant for Array::sort */ |
---|
47 | const int SORT_DECREASING = -1; |
---|
48 | |
---|
49 | /** |
---|
50 | Dynamic 1D array. |
---|
51 | |
---|
52 | Objects must have a default constructor (constructor that |
---|
53 | takes no arguments) in order to be used with this template. |
---|
54 | You will get the error "no appropriate default constructor found" |
---|
55 | if they do not. |
---|
56 | |
---|
57 | Do not use with objects that overload placement <code>operator new</code>, |
---|
58 | since the speed of Array is partly due to pooled allocation. |
---|
59 | |
---|
60 | If SSE is defined Arrays allocate the first element aligned to |
---|
61 | 16 bytes. |
---|
62 | |
---|
63 | |
---|
64 | Array is highly optimized compared to std::vector. |
---|
65 | Array operations are less expensive than on std::vector and for large |
---|
66 | amounts of data, Array consumes only 1.5x the total size of the |
---|
67 | data, while std::vector consumes 2.0x. The default |
---|
68 | array takes up zero heap space. The first resize (or append) |
---|
69 | operation grows it to a reasonable internal size so it is efficient |
---|
70 | to append to small arrays. Memory is allocated using |
---|
71 | System::alignedMalloc, which produces pointers aligned to 16-byte |
---|
72 | boundaries for use with SSE instructions and uses pooled storage for |
---|
73 | fast allocation. When Array needs to copy |
---|
74 | data internally on a resize operation it correctly invokes copy |
---|
75 | constructors of the elements (the MSVC6 implementation of |
---|
76 | std::vector uses realloc, which can create memory leaks for classes |
---|
77 | containing references and pointers). Array provides a guaranteed |
---|
78 | safe way to access the underlying data as a flat C array -- |
---|
79 | Array::getCArray. Although (T*)std::vector::begin() can be used for |
---|
80 | this purpose, it is not guaranteed to succeed on all platforms. |
---|
81 | |
---|
82 | To serialize an array, see G3D::serialize. |
---|
83 | |
---|
84 | Do not subclass an Array. |
---|
85 | */ |
---|
86 | template <class T> |
---|
87 | class Array { |
---|
88 | private: |
---|
89 | /** 0...num-1 are initialized elements, num...numAllocated-1 are not */ |
---|
90 | T* data; |
---|
91 | |
---|
92 | int num; |
---|
93 | int numAllocated; |
---|
94 | |
---|
95 | void init(int n, int a) { |
---|
96 | debugAssert(n <= a); |
---|
97 | debugAssert(n >= 0); |
---|
98 | this->num = 0; |
---|
99 | this->numAllocated = 0; |
---|
100 | data = NULL; |
---|
101 | if (a > 0) { |
---|
102 | resize(n); |
---|
103 | } else { |
---|
104 | data = NULL; |
---|
105 | } |
---|
106 | } |
---|
107 | |
---|
108 | void _copy(const Array &other) { |
---|
109 | init(other.num, other.num); |
---|
110 | for (int i = 0; i < num; i++) { |
---|
111 | data[i] = other.data[i]; |
---|
112 | } |
---|
113 | } |
---|
114 | |
---|
115 | /** |
---|
116 | Returns true iff address points to an element of this array. |
---|
117 | Used by append. |
---|
118 | */ |
---|
119 | inline bool inArray(const T* address) { |
---|
120 | return (address >= data) && (address < data + num); |
---|
121 | } |
---|
122 | |
---|
123 | |
---|
124 | /** Only compiled if you use the sort procedure. */ |
---|
125 | static bool __cdecl compareGT(const T& a, const T& b) { |
---|
126 | return a > b; |
---|
127 | } |
---|
128 | |
---|
129 | |
---|
130 | /** |
---|
131 | Allocates a new array of size numAllocated (not a parameter to the method) |
---|
132 | and then copies at most oldNum elements from the old array to it. Destructors are |
---|
133 | called for oldNum elements of the old array. |
---|
134 | */ |
---|
135 | void realloc(int oldNum) { |
---|
136 | T* oldData = data; |
---|
137 | |
---|
138 | // The allocation is separate from the constructor invocation because we don't want |
---|
139 | // to pay for the cost of constructors until the newly allocated |
---|
140 | // elements are actually revealed to the application. They |
---|
141 | // will be constructed in the resize() method. |
---|
142 | |
---|
143 | data = (T*)System::alignedMalloc(sizeof(T) * numAllocated, 16); |
---|
144 | |
---|
145 | // Call the copy constructors |
---|
146 | {const int N = iMin(oldNum, numAllocated); |
---|
147 | const T* end = data + N; |
---|
148 | T* oldPtr = oldData; |
---|
149 | for (T* ptr = data; ptr < end; ++ptr, ++oldPtr) { |
---|
150 | |
---|
151 | // Use placement new to invoke the constructor at the location |
---|
152 | // that we determined. Use the copy constructor to make the assignment. |
---|
153 | const T* constructed = new (ptr) T(*oldPtr); |
---|
154 | |
---|
155 | (void)constructed; |
---|
156 | debugAssertM(constructed == ptr, |
---|
157 | "new returned a different address than the one provided by Array."); |
---|
158 | }} |
---|
159 | |
---|
160 | // Call destructors on the old array (if there is no destructor, this will compile away) |
---|
161 | {const T* end = oldData + oldNum; |
---|
162 | for (T* ptr = oldData; ptr < end; ++ptr) { |
---|
163 | ptr->~T(); |
---|
164 | }} |
---|
165 | |
---|
166 | |
---|
167 | System::alignedFree(oldData); |
---|
168 | } |
---|
169 | |
---|
170 | public: |
---|
171 | |
---|
172 | /** |
---|
173 | C++ STL style iterator variable. Call begin() to get |
---|
174 | the first iterator, pre-increment (++i) the iterator to get to |
---|
175 | the next value. Use dereference (*i) to access the element. |
---|
176 | */ |
---|
177 | typedef T* Iterator; |
---|
178 | typedef const T* ConstIterator; |
---|
179 | |
---|
180 | /** |
---|
181 | C++ STL style iterator method. Returns the first iterator element. |
---|
182 | Do not change the size of the array while iterating. |
---|
183 | */ |
---|
184 | Iterator begin() { |
---|
185 | return data; |
---|
186 | } |
---|
187 | |
---|
188 | ConstIterator begin() const { |
---|
189 | return data; |
---|
190 | } |
---|
191 | /** |
---|
192 | C++ STL style iterator method. Returns one after the last iterator |
---|
193 | element. |
---|
194 | */ |
---|
195 | ConstIterator end() const { |
---|
196 | return data + num; |
---|
197 | } |
---|
198 | |
---|
199 | Iterator end() { |
---|
200 | return data + num; |
---|
201 | } |
---|
202 | |
---|
203 | /** |
---|
204 | The array returned is only valid until the next append() or resize call, or |
---|
205 | the Array is deallocated. |
---|
206 | */ |
---|
207 | T* getCArray() { |
---|
208 | return data; |
---|
209 | } |
---|
210 | |
---|
211 | /** |
---|
212 | The array returned is only valid until the next append() or resize call, or |
---|
213 | the Array is deallocated. |
---|
214 | */ |
---|
215 | const T* getCArray() const { |
---|
216 | return data; |
---|
217 | } |
---|
218 | |
---|
219 | /** Creates a zero length array (no heap allocation occurs until resize). */ |
---|
220 | Array() { |
---|
221 | init(0, 0); |
---|
222 | } |
---|
223 | |
---|
224 | /** |
---|
225 | Creates an array of size. |
---|
226 | */ |
---|
227 | Array(int size) { |
---|
228 | init(size, size); |
---|
229 | } |
---|
230 | |
---|
231 | /** |
---|
232 | Copy constructor |
---|
233 | */ |
---|
234 | Array(const Array& other) { |
---|
235 | _copy(other); |
---|
236 | } |
---|
237 | |
---|
238 | /** |
---|
239 | Destructor does not delete() the objects if T is a pointer type |
---|
240 | (e.g. T = int*) instead, it deletes the <B>pointers themselves</B> and |
---|
241 | leaves the objects. Call deleteAll if you want to dealocate |
---|
242 | the objects referenced. Do not call deleteAll if <CODE>T</CODE> is not a pointer |
---|
243 | type (e.g. do call Array<Foo*>::deleteAll, do <B>not</B> call Array<Foo>::deleteAll). |
---|
244 | */ |
---|
245 | ~Array() { |
---|
246 | // Invoke the destructors on the elements |
---|
247 | for (int i = 0; i < num; i++) { |
---|
248 | (data + i)->~T(); |
---|
249 | } |
---|
250 | |
---|
251 | System::alignedFree(data); |
---|
252 | // Set to 0 in case this Array is global and gets referenced during app exit |
---|
253 | data = NULL; |
---|
254 | num = 0; |
---|
255 | numAllocated = 0; |
---|
256 | } |
---|
257 | |
---|
258 | |
---|
259 | /** |
---|
260 | Removes all elements. Use resize(0, false) or fastClear if you want to |
---|
261 | remove all elements without deallocating the underlying array |
---|
262 | so that future append() calls will be faster. |
---|
263 | */ |
---|
264 | void clear() { |
---|
265 | resize(0); |
---|
266 | } |
---|
267 | |
---|
268 | /** resize(0, false) */ |
---|
269 | void fastClear() { |
---|
270 | resize(0, false); |
---|
271 | } |
---|
272 | |
---|
273 | /** |
---|
274 | Assignment operator. |
---|
275 | */ |
---|
276 | Array& operator=(const Array& other) { |
---|
277 | resize(other.num); |
---|
278 | for (int i = 0; i < num; ++i) { |
---|
279 | data[i] = other[i]; |
---|
280 | } |
---|
281 | return *this; |
---|
282 | } |
---|
283 | |
---|
284 | Array& operator=(const std::vector<T>& other) { |
---|
285 | resize((int)other.size()); |
---|
286 | for (int i = 0; i < num; ++i) { |
---|
287 | data[i] = other[i]; |
---|
288 | } |
---|
289 | return *this; |
---|
290 | } |
---|
291 | |
---|
292 | /** |
---|
293 | Number of elements in the array. |
---|
294 | */ |
---|
295 | inline int size() const { |
---|
296 | return num; |
---|
297 | } |
---|
298 | |
---|
299 | /** |
---|
300 | Number of elements in the array. (Same as size; this is just |
---|
301 | here for convenience). |
---|
302 | */ |
---|
303 | inline int length() const { |
---|
304 | return size(); |
---|
305 | } |
---|
306 | |
---|
307 | /** |
---|
308 | Swaps element index with the last element in the array then |
---|
309 | shrinks the array by one. |
---|
310 | */ |
---|
311 | void fastRemove(int index) { |
---|
312 | debugAssert(index >= 0); |
---|
313 | debugAssert(index < num); |
---|
314 | data[index] = data[num - 1]; |
---|
315 | resize(size() - 1); |
---|
316 | } |
---|
317 | |
---|
318 | /** |
---|
319 | Resizes, calling the default constructor for |
---|
320 | newly created objects and shrinking the underlying |
---|
321 | array as needed (and calling destructors as needed). |
---|
322 | */ |
---|
323 | void resize(int n) { |
---|
324 | resize(n, true); |
---|
325 | } |
---|
326 | |
---|
327 | /** Resizes without shrinking the underlying array */ |
---|
328 | void fastResize(int n) { |
---|
329 | resize(n, false); |
---|
330 | } |
---|
331 | |
---|
332 | |
---|
333 | /** |
---|
334 | Inserts at the specified index and shifts all other elements up by one. |
---|
335 | */ |
---|
336 | void insert(int n, const T& value) { |
---|
337 | // Add space for the extra element |
---|
338 | resize(num + 1, false); |
---|
339 | |
---|
340 | for (int i = num - 1; i > n; --i) { |
---|
341 | data[i] = data[i - 1]; |
---|
342 | } |
---|
343 | data[n] = value; |
---|
344 | } |
---|
345 | |
---|
346 | /** @param shrinkIfNecessary if false, memory will never be |
---|
347 | reallocated when the array shrinks. This makes resizing much |
---|
348 | faster but can waste memory. */ |
---|
349 | void resize(int n, bool shrinkIfNecessary) { |
---|
350 | int oldNum = num; |
---|
351 | num = n; |
---|
352 | |
---|
353 | // Call the destructors on newly hidden elements if there are any |
---|
354 | for (int i = num; i < oldNum; ++i) { |
---|
355 | (data + i)->~T(); |
---|
356 | } |
---|
357 | |
---|
358 | // Once allocated, always maintain 10 elements or 32 bytes, whichever is higher. |
---|
359 | static const int minSize = iMax(10, 32 / sizeof(T)); |
---|
360 | |
---|
361 | if (num > numAllocated) { |
---|
362 | // Grow the underlying array |
---|
363 | |
---|
364 | if (numAllocated == 0) { |
---|
365 | // First allocation; grow to exactly the size requested to avoid wasting space. |
---|
366 | numAllocated = n; |
---|
367 | debugAssert(oldNum == 0); |
---|
368 | realloc(oldNum); |
---|
369 | } else { |
---|
370 | |
---|
371 | if (num < minSize) { |
---|
372 | // Grow to at least the minimum size |
---|
373 | numAllocated = minSize; |
---|
374 | |
---|
375 | } else { |
---|
376 | |
---|
377 | // Increase the underlying size of the array. Grow aggressively |
---|
378 | // up to 64k, less aggressively up to 400k, and then grow relatively |
---|
379 | // slowly (1.5x per resize) to avoid excessive space consumption. |
---|
380 | // |
---|
381 | // These numbers are tweaked according to performance tests. |
---|
382 | |
---|
383 | float growFactor = 3.0; |
---|
384 | |
---|
385 | size_t oldSizeBytes = numAllocated * sizeof(T); |
---|
386 | if (oldSizeBytes > 400000) { |
---|
387 | // Avoid bloat |
---|
388 | growFactor = 1.5; |
---|
389 | } else if (oldSizeBytes > 64000) { |
---|
390 | // This is what std:: uses at all times |
---|
391 | growFactor = 2.0; |
---|
392 | } |
---|
393 | |
---|
394 | numAllocated = (num - numAllocated) + (int)(numAllocated * growFactor); |
---|
395 | |
---|
396 | if (numAllocated < minSize) { |
---|
397 | numAllocated = minSize; |
---|
398 | } |
---|
399 | } |
---|
400 | |
---|
401 | realloc(oldNum); |
---|
402 | } |
---|
403 | |
---|
404 | } else if ((num <= numAllocated / 3) && shrinkIfNecessary && (num > minSize)) { |
---|
405 | // Shrink the underlying array |
---|
406 | |
---|
407 | // Only copy over old elements that still remain after resizing |
---|
408 | // (destructors were called for others if we're shrinking) |
---|
409 | realloc(iMin(num, oldNum)); |
---|
410 | |
---|
411 | } |
---|
412 | |
---|
413 | // Call the constructors on newly revealed elements. |
---|
414 | // Do not use parens because we don't want the intializer |
---|
415 | // invoked for POD types. |
---|
416 | for (int i = oldNum; i < num; ++i) { |
---|
417 | new (data + i) T; |
---|
418 | } |
---|
419 | } |
---|
420 | |
---|
421 | /** |
---|
422 | Add an element to the end of the array. Will not shrink the underlying array |
---|
423 | under any circumstances. It is safe to append an element that is already |
---|
424 | in the array. |
---|
425 | */ |
---|
426 | inline void append(const T& value) { |
---|
427 | |
---|
428 | if (num < numAllocated) { |
---|
429 | // This is a simple situation; just stick it in the next free slot using |
---|
430 | // the copy constructor. |
---|
431 | new (data + num) T(value); |
---|
432 | ++num; |
---|
433 | } else if (inArray(&value)) { |
---|
434 | // The value was in the original array; resizing |
---|
435 | // is dangerous because it may move the value |
---|
436 | // we have a reference to. |
---|
437 | T tmp = value; |
---|
438 | append(tmp); |
---|
439 | } else { |
---|
440 | // Here we run the empty initializer where we don't have to, but |
---|
441 | // this simplifies the computation. |
---|
442 | resize(num + 1, DONT_SHRINK_UNDERLYING_ARRAY); |
---|
443 | data[num - 1] = value; |
---|
444 | } |
---|
445 | } |
---|
446 | |
---|
447 | |
---|
448 | inline void append(const T& v1, const T& v2) { |
---|
449 | if (inArray(&v1) || inArray(&v2)) { |
---|
450 | T t1 = v1; |
---|
451 | T t2 = v2; |
---|
452 | append(t1, t2); |
---|
453 | } else if (num + 1 < numAllocated) { |
---|
454 | // This is a simple situation; just stick it in the next free slot using |
---|
455 | // the copy constructor. |
---|
456 | new (data + num) T(v1); |
---|
457 | new (data + num + 1) T(v2); |
---|
458 | num += 2; |
---|
459 | } else { |
---|
460 | resize(num + 2, DONT_SHRINK_UNDERLYING_ARRAY); |
---|
461 | data[num - 2] = v1; |
---|
462 | data[num - 1] = v2; |
---|
463 | } |
---|
464 | } |
---|
465 | |
---|
466 | |
---|
467 | inline void append(const T& v1, const T& v2, const T& v3) { |
---|
468 | if (inArray(&v1) || inArray(&v2) || inArray(&v3)) { |
---|
469 | T t1 = v1; |
---|
470 | T t2 = v2; |
---|
471 | T t3 = v3; |
---|
472 | append(t1, t2, t3); |
---|
473 | } else if (num + 2 < numAllocated) { |
---|
474 | // This is a simple situation; just stick it in the next free slot using |
---|
475 | // the copy constructor. |
---|
476 | new (data + num) T(v1); |
---|
477 | new (data + num + 1) T(v2); |
---|
478 | new (data + num + 2) T(v3); |
---|
479 | num += 3; |
---|
480 | } else { |
---|
481 | resize(num + 3, DONT_SHRINK_UNDERLYING_ARRAY); |
---|
482 | data[num - 3] = v1; |
---|
483 | data[num - 2] = v2; |
---|
484 | data[num - 1] = v3; |
---|
485 | } |
---|
486 | } |
---|
487 | |
---|
488 | |
---|
489 | inline void append(const T& v1, const T& v2, const T& v3, const T& v4) { |
---|
490 | if (inArray(&v1) || inArray(&v2) || inArray(&v3) || inArray(&v4)) { |
---|
491 | T t1 = v1; |
---|
492 | T t2 = v2; |
---|
493 | T t3 = v3; |
---|
494 | T t4 = v4; |
---|
495 | append(t1, t2, t3, t4); |
---|
496 | } else if (num + 3 < numAllocated) { |
---|
497 | // This is a simple situation; just stick it in the next free slot using |
---|
498 | // the copy constructor. |
---|
499 | new (data + num) T(v1); |
---|
500 | new (data + num + 1) T(v2); |
---|
501 | new (data + num + 2) T(v3); |
---|
502 | new (data + num + 3) T(v4); |
---|
503 | num += 4; |
---|
504 | } else { |
---|
505 | resize(num + 4, DONT_SHRINK_UNDERLYING_ARRAY); |
---|
506 | data[num - 4] = v1; |
---|
507 | data[num - 3] = v2; |
---|
508 | data[num - 2] = v3; |
---|
509 | data[num - 1] = v4; |
---|
510 | } |
---|
511 | } |
---|
512 | |
---|
513 | /** |
---|
514 | Returns true if the given element is in the array. |
---|
515 | */ |
---|
516 | bool contains(const T& e) const { |
---|
517 | for (int i = 0; i < size(); ++i) { |
---|
518 | if ((*this)[i] == e) { |
---|
519 | return true; |
---|
520 | } |
---|
521 | } |
---|
522 | |
---|
523 | return false; |
---|
524 | } |
---|
525 | |
---|
526 | /** |
---|
527 | Append the elements of array. Cannot be called with this array |
---|
528 | as an argument. |
---|
529 | */ |
---|
530 | void append(const Array<T>& array) { |
---|
531 | debugAssert(this != &array); |
---|
532 | int oldNum = num; |
---|
533 | int arrayLength = array.length(); |
---|
534 | |
---|
535 | resize(num + arrayLength, false); |
---|
536 | |
---|
537 | for (int i = 0; i < arrayLength; i++) { |
---|
538 | data[oldNum + i] = array.data[i]; |
---|
539 | } |
---|
540 | } |
---|
541 | |
---|
542 | /** |
---|
543 | Pushes a new element onto the end and returns its address. |
---|
544 | This is the same as A.resize(A.size() + 1, false); A.last() |
---|
545 | */ |
---|
546 | inline T& next() { |
---|
547 | resize(num + 1, false); |
---|
548 | return last(); |
---|
549 | } |
---|
550 | |
---|
551 | /** |
---|
552 | Pushes an element onto the end (appends) |
---|
553 | */ |
---|
554 | inline void push(const T& value) { |
---|
555 | append(value); |
---|
556 | } |
---|
557 | |
---|
558 | inline void push(const Array<T>& array) { |
---|
559 | append(array); |
---|
560 | } |
---|
561 | |
---|
562 | /** Alias to provide std::vector compatibility */ |
---|
563 | inline void push_back(const T& v) { |
---|
564 | push(v); |
---|
565 | } |
---|
566 | |
---|
567 | /** "The member function removes the last element of the controlled sequence, which must be non-empty." |
---|
568 | For compatibility with std::vector. */ |
---|
569 | inline void pop_back() { |
---|
570 | pop(); |
---|
571 | } |
---|
572 | |
---|
573 | /** |
---|
574 | "The member function returns the storage currently allocated to hold the controlled |
---|
575 | sequence, a value at least as large as size()" |
---|
576 | For compatibility with std::vector. |
---|
577 | */ |
---|
578 | int capacity() const { |
---|
579 | return numAllocated; |
---|
580 | } |
---|
581 | |
---|
582 | /** |
---|
583 | "The member function returns a reference to the first element of the controlled sequence, |
---|
584 | which must be non-empty." |
---|
585 | For compatibility with std::vector. |
---|
586 | */ |
---|
587 | T& front() { |
---|
588 | return (*this)[0]; |
---|
589 | } |
---|
590 | |
---|
591 | /** |
---|
592 | "The member function returns a reference to the first element of the controlled sequence, |
---|
593 | which must be non-empty." |
---|
594 | For compatibility with std::vector. |
---|
595 | */ |
---|
596 | const T& front() const { |
---|
597 | return (*this)[0]; |
---|
598 | } |
---|
599 | |
---|
600 | /** |
---|
601 | Removes the last element and returns it. By default, shrinks the underlying array. |
---|
602 | */ |
---|
603 | inline T pop(bool shrinkUnderlyingArrayIfNecessary = true) { |
---|
604 | debugAssert(num > 0); |
---|
605 | T temp = data[num - 1]; |
---|
606 | resize(num - 1, shrinkUnderlyingArrayIfNecessary); |
---|
607 | return temp; |
---|
608 | } |
---|
609 | |
---|
610 | /** Pops the last element and discards it without returning anything. Faster than pop. |
---|
611 | By default, does not shrink the underlying array.*/ |
---|
612 | inline void popDiscard(bool shrinkUnderlyingArrayIfNecessary = false) { |
---|
613 | debugAssert(num > 0); |
---|
614 | resize(num - 1, shrinkUnderlyingArrayIfNecessary); |
---|
615 | } |
---|
616 | |
---|
617 | |
---|
618 | /** |
---|
619 | "The member function swaps the controlled sequences between *this and str." |
---|
620 | Note that this is slower than the optimal std implementation. |
---|
621 | |
---|
622 | For compatibility with std::vector. |
---|
623 | */ |
---|
624 | void swap(Array<T>& str) { |
---|
625 | Array<T> temp = str; |
---|
626 | str = *this; |
---|
627 | *this = temp; |
---|
628 | } |
---|
629 | |
---|
630 | |
---|
631 | /** |
---|
632 | Performs bounds checks in debug mode |
---|
633 | */ |
---|
634 | inline T& operator[](int n) { |
---|
635 | debugAssertM((n >= 0) && (n < num), format("Array index out of bounds. n = %d, size() = %d", n, num)); |
---|
636 | debugAssert(data!=NULL); |
---|
637 | return data[n]; |
---|
638 | } |
---|
639 | |
---|
640 | inline T& operator[](unsigned int n) { |
---|
641 | debugAssertM(((int)n < num), format("Array index out of bounds. n = %d, size() = %d", n, num)); |
---|
642 | return data[n]; |
---|
643 | } |
---|
644 | |
---|
645 | /** |
---|
646 | Performs bounds checks in debug mode |
---|
647 | */ |
---|
648 | inline const T& operator[](int n) const { |
---|
649 | debugAssert((n >= 0) && (n < num)); |
---|
650 | debugAssert(data!=NULL); |
---|
651 | return data[n]; |
---|
652 | } |
---|
653 | |
---|
654 | inline const T& operator[](unsigned int n) const { |
---|
655 | debugAssert((n < (unsigned int)num)); |
---|
656 | debugAssert(data!=NULL); |
---|
657 | return data[n]; |
---|
658 | } |
---|
659 | |
---|
660 | inline T& randomElement() { |
---|
661 | debugAssert(num > 0); |
---|
662 | debugAssert(data!=NULL); |
---|
663 | return data[iRandom(0, num - 1)]; |
---|
664 | } |
---|
665 | |
---|
666 | inline const T& randomElement() const { |
---|
667 | debugAssert(num > 0); |
---|
668 | debugAssert(data!=NULL); |
---|
669 | return data[iRandom(0, num - 1)]; |
---|
670 | } |
---|
671 | |
---|
672 | /** |
---|
673 | Returns the last element, performing a check in |
---|
674 | debug mode that there is at least one element. |
---|
675 | */ |
---|
676 | inline const T& last() const { |
---|
677 | debugAssert(num > 0); |
---|
678 | debugAssert(data!=NULL); |
---|
679 | return data[num - 1]; |
---|
680 | } |
---|
681 | |
---|
682 | /** Returns element lastIndex() */ |
---|
683 | inline T& last() { |
---|
684 | debugAssert(num > 0); |
---|
685 | debugAssert(data!=NULL); |
---|
686 | return data[num - 1]; |
---|
687 | } |
---|
688 | |
---|
689 | /** Returns <i>size() - 1</i> */ |
---|
690 | inline int lastIndex() const { |
---|
691 | debugAssertM(num > 0, "Array is empty"); |
---|
692 | return num - 1; |
---|
693 | } |
---|
694 | |
---|
695 | inline int firstIndex() const { |
---|
696 | debugAssertM(num > 0, "Array is empty"); |
---|
697 | return 0; |
---|
698 | } |
---|
699 | |
---|
700 | /** Returns element firstIndex(), performing a check in debug mode to ensure that there is at least one */ |
---|
701 | inline T& first() { |
---|
702 | debugAssertM(num > 0, "Array is empty"); |
---|
703 | return data[0]; |
---|
704 | } |
---|
705 | |
---|
706 | inline const T& first() const { |
---|
707 | debugAssertM(num > 0, "Array is empty"); |
---|
708 | return data[0]; |
---|
709 | } |
---|
710 | |
---|
711 | /** Returns iFloor(size() / 2), throws an assertion in debug mode if the array is empty */ |
---|
712 | inline int middleIndex() const { |
---|
713 | debugAssertM(num > 0, "Array is empty"); |
---|
714 | return num >> 1; |
---|
715 | } |
---|
716 | |
---|
717 | /** Returns element middleIndex() */ |
---|
718 | inline const T& middle() const { |
---|
719 | debugAssertM(num > 0, "Array is empty"); |
---|
720 | return data[num >> 1]; |
---|
721 | } |
---|
722 | |
---|
723 | /** Returns element middleIndex() */ |
---|
724 | inline T& middle() { |
---|
725 | debugAssertM(num > 0, "Array is empty"); |
---|
726 | return data[num >> 1]; |
---|
727 | } |
---|
728 | |
---|
729 | /** |
---|
730 | Calls delete on all objects[0...size-1] |
---|
731 | and sets the size to zero. |
---|
732 | */ |
---|
733 | void deleteAll() { |
---|
734 | for (int i = 0; i < num; i++) { |
---|
735 | delete data[i]; |
---|
736 | } |
---|
737 | resize(0); |
---|
738 | } |
---|
739 | |
---|
740 | /** |
---|
741 | Returns the index of (the first occurance of) an index or -1 if |
---|
742 | not found. |
---|
743 | */ |
---|
744 | int findIndex(const T& value) const { |
---|
745 | for (int i = 0; i < num; ++i) { |
---|
746 | if (data[i] == value) { |
---|
747 | return i; |
---|
748 | } |
---|
749 | } |
---|
750 | return -1; |
---|
751 | } |
---|
752 | |
---|
753 | /** |
---|
754 | Finds an element and returns the iterator to it. If the element |
---|
755 | isn't found then returns end(). |
---|
756 | */ |
---|
757 | Iterator find(const T& value) { |
---|
758 | for (int i = 0; i < num; ++i) { |
---|
759 | if (data[i] == value) { |
---|
760 | return data + i; |
---|
761 | } |
---|
762 | } |
---|
763 | return end(); |
---|
764 | } |
---|
765 | |
---|
766 | ConstIterator find(const T& value) const { |
---|
767 | for (int i = 0; i < num; ++i) { |
---|
768 | if (data[i] == value) { |
---|
769 | return data + i; |
---|
770 | } |
---|
771 | } |
---|
772 | return end(); |
---|
773 | } |
---|
774 | |
---|
775 | /** |
---|
776 | Removes count elements from the array |
---|
777 | referenced either by index or Iterator. |
---|
778 | */ |
---|
779 | void remove(Iterator element, int count = 1) { |
---|
780 | debugAssert((element >= begin()) && (element < end())); |
---|
781 | debugAssert((count > 0) && (element + count) <= end()); |
---|
782 | Iterator last = end() - count; |
---|
783 | |
---|
784 | while(element < last) { |
---|
785 | element[0] = element[count]; |
---|
786 | ++element; |
---|
787 | } |
---|
788 | |
---|
789 | resize(num - count); |
---|
790 | } |
---|
791 | |
---|
792 | void remove(int index, int count = 1) { |
---|
793 | debugAssert((index >= 0) && (index < num)); |
---|
794 | debugAssert((count > 0) && (index + count <= num)); |
---|
795 | |
---|
796 | remove(begin() + index, count); |
---|
797 | } |
---|
798 | |
---|
799 | /** |
---|
800 | Reverse the elements of the array in place. |
---|
801 | */ |
---|
802 | void reverse() { |
---|
803 | T temp; |
---|
804 | |
---|
805 | int n2 = num / 2; |
---|
806 | for (int i = 0; i < n2; ++i) { |
---|
807 | temp = data[num - 1 - i]; |
---|
808 | data[num - 1 - i] = data[i]; |
---|
809 | data[i] = temp; |
---|
810 | } |
---|
811 | } |
---|
812 | |
---|
813 | /** |
---|
814 | Sort using a specific less-than function, e.g.: |
---|
815 | |
---|
816 | <PRE> |
---|
817 | bool __cdecl myLT(const MyClass& elem1, const MyClass& elem2) { |
---|
818 | return elem1.x < elem2.x; |
---|
819 | } |
---|
820 | </PRE> |
---|
821 | |
---|
822 | Note that for pointer arrays, the <CODE>const</CODE> must come |
---|
823 | <I>after</I> the class name, e.g., <CODE>Array<MyClass*></CODE> uses: |
---|
824 | |
---|
825 | <PRE> |
---|
826 | bool __cdecl myLT(MyClass*const& elem1, MyClass*const& elem2) { |
---|
827 | return elem1->x < elem2->x; |
---|
828 | } |
---|
829 | </PRE> |
---|
830 | */ |
---|
831 | void sort(bool (__cdecl *lessThan)(const T& elem1, const T& elem2)) { |
---|
832 | std::sort(data, data + num, lessThan); |
---|
833 | } |
---|
834 | |
---|
835 | |
---|
836 | /** |
---|
837 | Sorts the array in increasing order using the > or < operator. To |
---|
838 | invoke this method on Array<T>, T must override those operator. |
---|
839 | You can overide these operators as follows: |
---|
840 | <code> |
---|
841 | bool T::operator>(const T& other) const { |
---|
842 | return ...; |
---|
843 | } |
---|
844 | bool T::operator<(const T& other) const { |
---|
845 | return ...; |
---|
846 | } |
---|
847 | </code> |
---|
848 | */ |
---|
849 | void sort(int direction = SORT_INCREASING) { |
---|
850 | if (direction == SORT_INCREASING) { |
---|
851 | std::sort(data, data + num); |
---|
852 | } else { |
---|
853 | std::sort(data, data + num, compareGT); |
---|
854 | } |
---|
855 | } |
---|
856 | |
---|
857 | /** |
---|
858 | Sorts elements beginIndex through and including endIndex. |
---|
859 | */ |
---|
860 | void sortSubArray(int beginIndex, int endIndex, int direction = SORT_INCREASING) { |
---|
861 | if (direction == SORT_INCREASING) { |
---|
862 | std::sort(data + beginIndex, data + endIndex + 1); |
---|
863 | } else { |
---|
864 | std::sort(data + beginIndex, data + endIndex + 1, compareGT); |
---|
865 | } |
---|
866 | } |
---|
867 | |
---|
868 | void sortSubArray(int beginIndex, int endIndex, bool (__cdecl *lessThan)(const T& elem1, const T& elem2)) { |
---|
869 | std::sort(data + beginIndex, data + endIndex + 1, lessThan); |
---|
870 | } |
---|
871 | |
---|
872 | /** |
---|
873 | The StrictWeakOrdering can be either a class that overloads the function call operator() or |
---|
874 | a function pointer of the form <code>bool (__cdecl *lessThan)(const T& elem1, const T& elem2)</code> |
---|
875 | */ |
---|
876 | template<typename StrictWeakOrdering> |
---|
877 | void sortSubArray(int beginIndex, int endIndex, StrictWeakOrdering& lessThan) { |
---|
878 | std::sort(data + beginIndex, data + endIndex + 1, lessThan); |
---|
879 | } |
---|
880 | |
---|
881 | /** Uses < and == to evaluate operator(); this is the default comparator for Array::partition. */ |
---|
882 | class DefaultComparator { |
---|
883 | public: |
---|
884 | inline int operator()(const T& A, const T& B) const { |
---|
885 | if (A < B) { |
---|
886 | return 1; |
---|
887 | } else if (A == B) { |
---|
888 | return 0; |
---|
889 | } else { |
---|
890 | return -1; |
---|
891 | } |
---|
892 | } |
---|
893 | }; |
---|
894 | |
---|
895 | /** The output arrays are resized with fastClear() so that if they are already of the same size |
---|
896 | as this array no memory is allocated during partitioning. |
---|
897 | |
---|
898 | @param comparator A function, or class instance with an overloaded operator() that compares |
---|
899 | two elements of type <code>T</code> and returns 0 if they are equal, -1 if the second is smaller, |
---|
900 | and 1 if the first is smaller (i.e., following the conventions of std::string::compare). For example: |
---|
901 | |
---|
902 | <pre> |
---|
903 | int compare(int A, int B) { |
---|
904 | if (A < B) { |
---|
905 | return 1; |
---|
906 | } else if (A == B) { |
---|
907 | return 0; |
---|
908 | } else { |
---|
909 | return -1; |
---|
910 | } |
---|
911 | } |
---|
912 | </pre> |
---|
913 | */ |
---|
914 | template<typename Comparator> |
---|
915 | void partition( |
---|
916 | const T& partitionElement, |
---|
917 | Array<T>& ltArray, |
---|
918 | Array<T>& eqArray, |
---|
919 | Array<T>& gtArray, |
---|
920 | const Comparator& comparator) const { |
---|
921 | |
---|
922 | // Make sure all arrays are independent |
---|
923 | debugAssert(<Array != this); |
---|
924 | debugAssert(&eqArray != this); |
---|
925 | debugAssert(>Array != this); |
---|
926 | debugAssert(<Array != &eqArray); |
---|
927 | debugAssert(<Array != >Array); |
---|
928 | debugAssert(&eqArray != >Array); |
---|
929 | |
---|
930 | // Clear the arrays |
---|
931 | ltArray.fastClear(); |
---|
932 | eqArray.fastClear(); |
---|
933 | gtArray.fastClear(); |
---|
934 | |
---|
935 | // Form a table of buckets for lt, eq, and gt |
---|
936 | Array<T>* bucket[3] = {<Array, &eqArray, >Array}; |
---|
937 | |
---|
938 | for (int i = 0; i < num; ++i) { |
---|
939 | int c = comparator(partitionElement, data[i]); |
---|
940 | debugAssertM(c >= -1 && c <= 1, "Comparator returned an illegal value."); |
---|
941 | |
---|
942 | // Insert into the correct bucket, 0, 1, or 2 |
---|
943 | bucket[c + 1]->append(data[i]); |
---|
944 | } |
---|
945 | } |
---|
946 | |
---|
947 | /** |
---|
948 | Uses < and == on elements to perform a partition. See partition(). |
---|
949 | */ |
---|
950 | void partition( |
---|
951 | const T& partitionElement, |
---|
952 | Array<T>& ltArray, |
---|
953 | Array<T>& eqArray, |
---|
954 | Array<T>& gtArray) const { |
---|
955 | |
---|
956 | partition(partitionElement, ltArray, eqArray, gtArray, typename Array<T>::DefaultComparator()); |
---|
957 | } |
---|
958 | |
---|
959 | /** |
---|
960 | Paritions the array into those below the median, those above the median, and those elements |
---|
961 | equal to the median in expected O(n) time using quickselect. If the array has an even |
---|
962 | number of different elements, the median for partition purposes is the largest value |
---|
963 | less than the median. |
---|
964 | |
---|
965 | @param tempArray used for working scratch space |
---|
966 | @param comparator see parition() for a discussion.*/ |
---|
967 | template<typename Comparator> |
---|
968 | void medianPartition( |
---|
969 | Array<T>& ltMedian, |
---|
970 | Array<T>& eqMedian, |
---|
971 | Array<T>& gtMedian, |
---|
972 | Array<T>& tempArray, |
---|
973 | const Comparator& comparator) const { |
---|
974 | |
---|
975 | ltMedian.fastClear(); |
---|
976 | eqMedian.fastClear(); |
---|
977 | gtMedian.fastClear(); |
---|
978 | |
---|
979 | // Handle trivial cases first |
---|
980 | switch (size()) { |
---|
981 | case 0: |
---|
982 | // Array is empty; no parition is possible |
---|
983 | return; |
---|
984 | |
---|
985 | case 1: |
---|
986 | // One element |
---|
987 | eqMedian.append(first()); |
---|
988 | return; |
---|
989 | |
---|
990 | case 2: |
---|
991 | { |
---|
992 | // Two element array; median is the smaller |
---|
993 | int c = comparator(first(), last()); |
---|
994 | |
---|
995 | switch (c) { |
---|
996 | case -1: |
---|
997 | // first was bigger |
---|
998 | eqMedian.append(last()); |
---|
999 | gtMedian.append(first()); |
---|
1000 | break; |
---|
1001 | |
---|
1002 | case 0: |
---|
1003 | // Both equal to the median |
---|
1004 | eqMedian.append(first(), last()); |
---|
1005 | break; |
---|
1006 | |
---|
1007 | case 1: |
---|
1008 | // Last was bigger |
---|
1009 | eqMedian.append(first()); |
---|
1010 | gtMedian.append(last()); |
---|
1011 | break; |
---|
1012 | } |
---|
1013 | } |
---|
1014 | return; |
---|
1015 | } |
---|
1016 | |
---|
1017 | // All other cases use a recursive randomized median |
---|
1018 | |
---|
1019 | // Number of values less than all in the current arrays |
---|
1020 | int ltBoost = 0; |
---|
1021 | |
---|
1022 | // Number of values greater than all in the current arrays |
---|
1023 | int gtBoost = 0; |
---|
1024 | |
---|
1025 | // For even length arrays, force the gt array to be one larger than the |
---|
1026 | // lt array: |
---|
1027 | // [1 2 3] size = 3, choose half = (s + 1) /2 |
---|
1028 | // |
---|
1029 | int lowerHalfSize, upperHalfSize; |
---|
1030 | if (isEven(size())) { |
---|
1031 | lowerHalfSize = size() / 2; |
---|
1032 | upperHalfSize = lowerHalfSize + 1; |
---|
1033 | } else { |
---|
1034 | lowerHalfSize = upperHalfSize = (size() + 1) / 2; |
---|
1035 | } |
---|
1036 | const T* xPtr = NULL; |
---|
1037 | |
---|
1038 | // Maintain pointers to the arrays; we'll switch these around during sorting |
---|
1039 | // to avoid copies. |
---|
1040 | const Array<T>* source = this; |
---|
1041 | Array<T>* lt = <Median; |
---|
1042 | Array<T>* eq = &eqMedian; |
---|
1043 | Array<T>* gt = >Median; |
---|
1044 | Array<T>* extra = &tempArray; |
---|
1045 | |
---|
1046 | while (true) { |
---|
1047 | // Choose a random element -- choose the middle element; this is theoretically |
---|
1048 | // suboptimal, but for loosly sorted array is actually the best strategy |
---|
1049 | |
---|
1050 | xPtr = &(source->middle()); |
---|
1051 | if (source->size() == 1) { |
---|
1052 | // Done; there's only one element left |
---|
1053 | break; |
---|
1054 | } |
---|
1055 | const T& x = *xPtr; |
---|
1056 | |
---|
1057 | // Note: partition (fast) clears the arrays for us |
---|
1058 | source->partition(x, *lt, *eq, *gt, comparator); |
---|
1059 | |
---|
1060 | int L = lt->size() + ltBoost + eq->size(); |
---|
1061 | int U = gt->size() + gtBoost + eq->size(); |
---|
1062 | if ((L >= lowerHalfSize) && |
---|
1063 | (U >= upperHalfSize)) { |
---|
1064 | |
---|
1065 | // x must be the partition median |
---|
1066 | break; |
---|
1067 | |
---|
1068 | } else if (L < lowerHalfSize) { |
---|
1069 | |
---|
1070 | // x must be smaller than the median. Recurse into the 'gt' array. |
---|
1071 | ltBoost += lt->size() + eq->size(); |
---|
1072 | |
---|
1073 | // The new gt array will be the old source array, unless |
---|
1074 | // that was the this pointer (i.e., unless we are on the |
---|
1075 | // first iteration) |
---|
1076 | Array<T>* newGt = (source == this) ? extra : const_cast<Array<T>*>(source); |
---|
1077 | |
---|
1078 | // Now set up the gt array as the new source |
---|
1079 | source = gt; |
---|
1080 | gt = newGt; |
---|
1081 | |
---|
1082 | } else { |
---|
1083 | |
---|
1084 | // x must be bigger than the median. Recurse into the 'lt' array. |
---|
1085 | gtBoost += gt->size() + eq->size(); |
---|
1086 | |
---|
1087 | // The new lt array will be the old source array, unless |
---|
1088 | // that was the this pointer (i.e., unless we are on the |
---|
1089 | // first iteration) |
---|
1090 | Array<T>* newLt = (source == this) ? extra : const_cast<Array<T>*>(source); |
---|
1091 | |
---|
1092 | // Now set up the lt array as the new source |
---|
1093 | source = lt; |
---|
1094 | lt = newLt; |
---|
1095 | } |
---|
1096 | } |
---|
1097 | |
---|
1098 | // Now that we know the median, make a copy of it (since we're about to destroy the array that it |
---|
1099 | // points into). |
---|
1100 | T median = *xPtr; |
---|
1101 | xPtr = NULL; |
---|
1102 | |
---|
1103 | // Partition the original array (note that this fast clears for us) |
---|
1104 | partition(median, ltMedian, eqMedian, gtMedian, comparator); |
---|
1105 | } |
---|
1106 | |
---|
1107 | /** |
---|
1108 | Computes a median partition using the default comparator and a dynamically allocated temporary |
---|
1109 | working array. If the median is not in the array, it is chosen to be the largest value smaller |
---|
1110 | than the true median. |
---|
1111 | */ |
---|
1112 | void medianPartition( |
---|
1113 | Array<T>& ltMedian, |
---|
1114 | Array<T>& eqMedian, |
---|
1115 | Array<T>& gtMedian) const { |
---|
1116 | |
---|
1117 | Array<T> temp; |
---|
1118 | medianPartition(ltMedian, eqMedian, gtMedian, temp, DefaultComparator()); |
---|
1119 | } |
---|
1120 | |
---|
1121 | |
---|
1122 | /** Redistributes the elements so that the new order is statistically independent |
---|
1123 | of the original order. O(n) time.*/ |
---|
1124 | void randomize() { |
---|
1125 | T temp; |
---|
1126 | |
---|
1127 | for (int i = size() - 1; i >= 0; --i) { |
---|
1128 | int x = iRandom(0, i); |
---|
1129 | |
---|
1130 | temp = data[i]; |
---|
1131 | data[i] = data[x]; |
---|
1132 | data[x] = temp; |
---|
1133 | } |
---|
1134 | } |
---|
1135 | |
---|
1136 | |
---|
1137 | }; |
---|
1138 | |
---|
1139 | |
---|
1140 | /** Array::contains for C-arrays */ |
---|
1141 | template<class T> bool contains(const T* array, int len, const T& e) { |
---|
1142 | for (int i = len - 1; i >= 0; --i) { |
---|
1143 | if (array[i] == e) { |
---|
1144 | return true; |
---|
1145 | } |
---|
1146 | } |
---|
1147 | return false; |
---|
1148 | } |
---|
1149 | |
---|
1150 | } // namespace |
---|
1151 | |
---|
1152 | #endif |
---|
1153 | |
---|
1154 | #ifdef G3D_WIN32 |
---|
1155 | # pragma warning (push) |
---|
1156 | #endif |
---|