Richard Boegli's CnC_Generals_Zero_Hour Fork WIP
This is documentation of Richard Boegil's Zero Hour Fork
 
Loading...
Searching...
No Matches
arraylist.h
Go to the documentation of this file.
1/*
2** Command & Conquer Generals Zero Hour(tm)
3** Copyright 2025 Electronic Arts Inc.
4**
5** This program is free software: you can redistribute it and/or modify
6** it under the terms of the GNU General Public License as published by
7** the Free Software Foundation, either version 3 of the License, or
8** (at your option) any later version.
9**
10** This program is distributed in the hope that it will be useful,
11** but WITHOUT ANY WARRANTY; without even the implied warranty of
12** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13** GNU General Public License for more details.
14**
15** You should have received a copy of the GNU General Public License
16** along with this program. If not, see <http://www.gnu.org/licenses/>.
17*/
18
19/****************************************************************************\
20* C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *
21******************************************************************************
22Project Name:
23File Name : arraylist.h
24Author : Neal Kettler
25Start Date : Jan 19, 1997
26Last Update : Jan 19, 1997
27------------------------------------------------------------------------------
28
29Array implementation of a list. Note: There are some freaky C++ memory tricks
30going on here. If you think there's a leak, see me before changing it.
31The way this works is that it allocates an array to hold 'N' items on the
32first list add. It doesn't call the constructors for those 'N' items until
33necessary (for efficiency). When an item is added to a slot then a new
34class is constructed inside the array element using the placement new operator
35and the class's copy constructor. When elements are removed the destructor
36is then manually called on this memory location.
37
38All data added to the list is copied so feel free to delete/destroy/modify
39the original after an add.
40
41You _must_ have a good copy constructor for any classes that you use this template
42for! A good copy constructor is one that won't blindly duplicate pointers
43that don't belong to them, etc...
44
45\****************************************************************************/
46
47#ifndef ARRAYLIST_HEADER
48#define ARRAYLIST_HEADER
49
50#include <stdio.h>
51#include <stdlib.h>
52#include <string.h>
53#include <assert.h>
54#include <new.h>
55#include <math.h>
56
57#include "wstypes.h"
58
59//
60// Usage: ArrayList<int> TheList;
61//
62template <class T>
63class ArrayList
64{
65 public:
69
70 // Remove all entries from the lsit
71 void clear(void);
72
73 // Add a node after the zero based 'pos'
74 bit8 add(IN T &node,sint32 pos);
75 bit8 addTail(IN T &node);
76 bit8 addHead(IN T &node);
77 bit8 addSortedAsc(IN T &node); // Ascending
78 bit8 addSortedDes(IN T &node); // Descending
79 /*bit8 addNumSortedAsc(IN T &node); // Ascending
80 bit8 addNumSortedDes(IN T &node); // Descending*/
81
82 // Remove a node
83 bit8 remove(OUT T &node,sint32 pos);
86 bit8 removeTail(OUT T &node);
87
88 // Replace one obj with another
89 bit8 replace(IN T &node, sint32 pos);
90
91
92 // Get a node without removing from the list
93 bit8 get(OUT T &node,sint32 pos) RO;
94 bit8 getHead(OUT T &node) RO;
95 bit8 getTail(OUT T &node) RO;
96
97 // Get a pointer to the interally managed copy (careful!)
98 bit8 getPointer(OUT T **node,sint32 pos) RO;
99
100 // Get the number of entries in the list
102
103 // UNSAFE! for classes, see note below!
104 bit8 setSize(sint32 newsize, IN T &filler);
105
106 // Print information on the list
107 void print(FILE *out);
108
109 // assignment operator
111
112 private:
113 sint32 _sortedLookup(IN T &target, int ascending);
114 sint32 Entries_; // Number of entries
115 sint32 Slots_; // Number of available slots
116
117 T *Vector_; // The actual memory where the list is held
118
119 enum
120 {
121 INITIAL_SIZE = 10
122 };
123
124 bit8 growVector(void); // Expand the number of slots
125 bit8 shrinkVector(void); // Reduce the number of slots
126};
127
128
129//Create the empty list
130template <class T>
132{
133 Entries_=0;
134 Slots_=0;
135 Vector_=NULL;
136}
137
138// copy constructor
139template <class T>
141{
142 Entries_=0;
143 Slots_=0;
144 Vector_=NULL;
145 (*this)=other;
146}
147
148//Free all the memory...
149template <class T>
151{
152 clear(); // Remove the entries & call destructors on them
153
154 delete[]((uint8*)Vector_); // this will prevent the destructors from
155 // gettting called on elements not
156 // containing valid objects.
157
158 //fprintf(stderr,"Arraylist destructor\n");
159}
160
161// assignment operator
162template <class T>
164{
165 T node;
166 clear();
167 for (int i=0; i<other.length(); i++)
168 {
169 other.get(node,i);
170 addTail(node);
171 }
172 return(*this);
173}
174
175
176// Remove all the entries and free the memory
177template <class T>
179{
180 for (int i=0; i<Entries_; i++)
181 {
182 (Vector_+i)->~T(); // Call the destructor manually. Don't try this
183 // at home kiddies!
184 }
185 Entries_=0;
186}
187
188// ************************* UNSAFE UNSAFE UNSAFE *************************
189// Note - Don't call this on any type with a constructor/destructor since this
190// is really dumb and just puts a new one of filler in. Well, it's kindof safe
191// just be careful.
192// It's fine for simple classes like ints though..
193//
194// Add/remove entries in a stupid manner...
195//
196// **************************************************************************
197template <class T>
198bit8 ArrayList<T>::setSize(sint32 newsize, IN T &filler)
199{
200 int oldEntries=Entries_;
201 Entries_ = newsize;
202
203 if (newsize<0)
204 return(false);
205
206 // Grow the vector as much as we need to
207 while (newsize > Slots_)
208 growVector();
209
210 // Create new objects in the blank holes
211 for (int i=oldEntries; i<Entries_; i++)
212 {
213 // Now put the replacement object in there...
214 new((void *)(Vector_+i)) T(filler); // Trust me, this isn't a memory leak
215 }
216
217 // If we're at 33% usage or less, shrink the vector
218 if ((Entries_*3) <= Slots_) // don't do while, because I think shrink will never goto 0
219 shrinkVector();
220
221 return(true);
222}
223
224
225// When adding into a position, the new node goes at the zero based slot
226// specified by pos. All other nodes get moved one slot down.
227template <class T>
228bit8 ArrayList<T>::add(IN T &node,sint32 pos)
229{
230 if (pos > Entries_) // You can only access one of the end of the vector
231 pos=Entries_;
232 if (pos >= Slots_) // If we're at the end, grow the list
233 growVector();
234 if (Entries_ >= Slots_) // enuff space?
235 growVector();
236
237 // If we are insering into the middle or front of the list we have to
238 // slide the old objects forward.
239 if (pos < Entries_) // If there are elements after the add point
240 memmove(Vector_+pos+1,Vector_+pos,sizeof(T)*(Entries_-pos)); // move them forward
241
242 //fprintf(stderr,"Placement new to %p\n",(Vector_+pos));
243
244 // This uses the placement new operator. placement new allows us to
245 // specify the memory address for the new object. In this case we
246 // want it at the 'pos' index into our array.
247 new((void *)(Vector_+pos)) T((T &)node); // Trust me, this isn't a memory leak
248 Entries_++; // one new entry
249 return(TRUE);
250}
251
252
253// Add to the first node, all others get shifted down one slot
254template <class T>
256{
257 return(add(node,0));
258}
259
260
261// Append to the end of the list
262template <class T>
264{
265 return(add(node,length()));
266}
267
268
269// addSortedX only works (properly) if evrerything else in the list is added
270// using addSorted.
271template <class T>
273{
274 sint32 pos = _sortedLookup(node, 1);
275 return(add(node, pos));
276}
277
278
279// addSortedX only works (properly) if evrerything else in the list is added
280// using addSorted.
281template <class T>
283{
284 sint32 pos = _sortedLookup(node, 0);
285 return(add(node, pos));
286}
287
288
289// This is the binary search used by addSorted
290template <class T>
291sint32 ArrayList<T>::_sortedLookup(IN T &target, int ascending)
292{
293 int low, mid, high;
294 T* lowtarget;
295 T* hightarget;
296 T* midtarget;
297
298
299 // Trivial cases
300 if( Entries_ == 0 )
301 return 0;
302
303 low = 0;
304 high = Entries_ - 1;
305 while( 1 )
306 {
307 assert( low <= high );
308 mid = low + (int)(floor(((double)high - (double)low) / (double)2));
309
310 getPointer(&lowtarget, low);
311 getPointer(&hightarget, high);
312 getPointer(&midtarget, mid);
313
314 // Exact match
315 if( *midtarget == target ) return mid;
316
317 // Single element
318 if( high == low )
319 {
320 if( ascending )
321 {
322 if( target <= *lowtarget )
323 return low;
324 else
325 return low + 1;
326 }
327 else
328 {
329 if( target <= *lowtarget )
330 return low + 1;
331 else
332 return low;
333 }
334 }
335
336 // Two elemsnts
337 if( (high - low) == 1 )
338 {
339 if( ascending )
340 {
341 if( target <= *lowtarget )
342 return low;
343 else if( target <= *hightarget )
344 return high;
345 else
346 return high + 1;
347 }
348 else
349 {
350 if( target <= *hightarget )
351 return high + 1;
352 else if( target <= *lowtarget )
353 return high;
354 else
355 return low;
356 }
357 }
358
359 // Sorry, try again...
360 if( ascending )
361 {
362 if( target < *midtarget )
363 high = mid;
364 else
365 low = mid;
366 }
367 else
368 {
369 if( target < *midtarget )
370 low = mid;
371 else
372 high = mid;
373 }
374 }
375}
376
377
378/*// addNumSortedX works in much the same way as addSortedX, except that I needed
379// it for a very specific thing. I needed a list of strings numerically sorted,
380// not alphabetically sorted. Furthermore these strings were composed of numbers
381// delimited by underscores. In the interest of keeping it generic, these
382// functions take as args a node, a delimiting character, and a count of the
383// number of fields to include in a sort. If this is the list of strings:
384//
385// 55_100, 2_5, 23_32, 98_445, 2_48, 8_88, 2_3, 2_4
386//
387// An alphabetical sort is:
388//
389// 2_3, 2_4, 2_48, 2_5, 55_100, 8_88, 98_445
390//
391// But a numerical sort by calling addNumSortedAsc(<whatever>, "_", 2) will result in:
392//
393// 2_3, 2_4, 2_5, 2_48, 8_88, 55_100, 98_445
394//
395// Yes...now that you mention it I am on crack...
396//
397template <class T>
398bit8 ArrayList<T>::addNumSortedAsc(IN T &node, char delim, int fields)
399{
400 sint32 pos = _numSortedLookup(node, delim, fields, 1);
401 return(add(node, pos));
402}
403
404
405// See addNumSortedAsc comment above.
406template <class T>
407bit8 ArrayList<T>::addSortedDes(IN T &node, char delim, int fields)
408{
409 sint32 pos = _sortedLookup(node, delim, fields, 0);
410 return(add(node, pos));
411}
412
413
414// This is the binary search used by addSorted
415template <class T>
416sint32 ArrayList<T>::_numSortedLookup(IN T &target, char delim, int fields, int ascending)
417{
418 int low, mid, high;
419 T* lowtarget;
420 T* hightarget;
421 T* midtarget;
422
423
424 // Trivial case
425 if( Entries_ == 0 )
426 return 0;
427
428 low = 0;
429 high = Entries_;
430 while( 1 )
431 {
432 assert( low <= high );
433 mid = low + (int)(floor(((double)high - (double)low) / (double)2));
434
435 getPointer(&lowtarget, low);
436 getPointer(&hightarget, high);
437 getPointer(&midtarget, mid);
438
439 // Exact match
440 if( *midtarget == target ) return mid;
441
442 // Single element
443 if( high == low )
444 {
445 if( ascending )
446 {
447 if( target <= *lowtarget )
448 return low;
449 else
450 return low + 1;
451 }
452 else
453 {
454 if( target <= *lowtarget )
455 return low + 1;
456 else
457 return low;
458 }
459 }
460
461 // Two elemsnts
462 if( (high - low) == 1 )
463 {
464 if( ascending )
465 {
466 if( target <= *lowtarget )
467 return low;
468 else
469 return high;
470 }
471 else
472 {
473 if( target <= *lowtarget )
474 return high;
475 else
476 return low;
477 }
478 }
479
480 // Sorry, try again...
481 if( ascending )
482 {
483 if( target < *midtarget )
484 high = mid;
485 else
486 low = mid;
487 }
488 else
489 {
490 if( target < *midtarget )
491 low = mid;
492 else
493 high = mid;
494 }
495 }
496}*/
497
498
499//
500// Delete an item at this index and construct a new one in it's place
501//
502template <class T>
504{
505 if (Entries_==0)
506 return(FALSE);
507 if (pos<0)
508 pos=0;
509 if (pos >= Entries_)
510 pos=Entries_-1;
511
512 (Vector_+pos)->~T(); // Call the destructor manually. Don't try this
513 // at home kiddies!
514
515 // Now put the replacement object in there...
516 new((void *)(Vector_+pos)) T(node); // Trust me, this isn't a memory leak
517
518 return(TRUE);
519}
520
521
522
523// Remove at the zero based index specified by 'pos'. When removing from
524// a slot, all others get shifted up by one.
525template <class T>
527{
528 if (Entries_==0)
529 return(FALSE);
530 if (pos<0)
531 pos=0;
532 if (pos >= Entries_)
533 pos=Entries_-1;
534
535 (Vector_+pos)->~T(); // Call the destructor manually. Don't try this
536 // at home kiddies!
537
538 memmove(Vector_+pos,Vector_+pos+1,sizeof(T)*(Entries_-pos-1));
539
540 Entries_--;
541
542 // If we're at 33% usage or less, shrink the vector
543 if ( (Entries_*3) <= Slots_)
544 shrinkVector();
545
546 return(TRUE);
547}
548
549
550// Remove at the zero based index specified by 'pos'. When removing from
551// a slot, all others get shifted up by one.
552template <class T>
554{
555 bit8 retval;
556 retval=get(node,pos);
557 if (retval==FALSE)
558 return(FALSE);
559 return(remove(pos));
560}
561
562
563// Remove the first node of the list
564template <class T>
566{
567 return(remove(node,0));
568}
569
570
571// Remove the last node of the list
572template <class T>
574{
575 return(remove(node,Entries_-1));
576}
577
578// get a pointer to the internally managed object. Try and avoid this, but
579// sometimes efficiency requires it...
580// get a copy of an item
581template <class T>
583{
584 if ((pos < 0)||(pos >= Entries_))
585 return(FALSE);
586 *node=&(Vector_[pos]);
587 return(TRUE);
588}
589
590
591// get a copy of an item
592template <class T>
593bit8 ArrayList<T>::get(OUT T &node,sint32 pos) RO
594{
595 if ((pos < 0)||(pos >= Entries_))
596 return(FALSE);
597 node=Vector_[pos];
598 return(TRUE);
599}
600
601
602// get a copy of the first node of the list
603template <class T>
605{
606 return(get(node,0));
607}
608
609
610// get a copy of the last node
611template <class T>
613{
614 return(get(node,Entries_-1));
615}
616
617// just for debugging
618template <class T>
619void ArrayList<T>::print(FILE *out)
620{
621 fprintf(out,"--------------------\n");
622 //for (int i=0; i<Entries_; i++)
623 // Vector_[i].print();
624 fprintf(out,"Entries: %d Slots: %d sizeof(T): %d\n",Entries_,Slots_,
625 sizeof(T));
626 fprintf(out,"--------------------\n");
627}
628
629// Return the current length of the list
630template <class T>
632{
633 return(Entries_);
634}
635
636// Grow the vector by a factor of 2X
637template <class T>
638bit8 ArrayList<T>::growVector(void)
639{
640 if (Entries_ < Slots_) // Don't grow until we're at 100% usage
641 return(FALSE);
642
643 int newSlots=Entries_*2;
644 if(newSlots < INITIAL_SIZE)
645 newSlots=INITIAL_SIZE;
646
647 //fprintf(stderr,"Growing vector to: %d\n",newSlots);
648
649 // The goofy looking new below prevents operator new from getting called
650 // unnecessarily. This is severall times faster than allocating all of
651 // the slots as objects and then calling the assignment operator on them
652 // when they actually get used.
653 //
654 T *newVector=(T *)(new uint8[newSlots * sizeof(T)]);
655 memset(newVector,0,newSlots * sizeof(T)); // zero just to be safe
656
657 if (Vector_ != NULL)
658 memcpy(newVector,Vector_,Entries_*sizeof(T));
659
660 delete[]((uint8 *)Vector_); // Get rid of the old vector without calling
661 // destructors
662
663 Vector_=newVector;
664 Slots_=newSlots;
665
666 return(TRUE);
667}
668
669
670// Shrink the vector by a factor of 2X
671template <class T>
672bit8 ArrayList<T>::shrinkVector(void)
673{
674 //fprintf(stderr,"Shrink called\n");
675
676 // Don't need to shrink until usage goes below 33%
677 if ( (Entries_*3) > Slots_)
678 return(FALSE);
679
680 int newSlots=Slots_/2;
681 if(newSlots < INITIAL_SIZE) // never shrink past initial size
682 newSlots=INITIAL_SIZE;
683
684 if (newSlots >= Slots_) // don't need to shrink
685 return(FALSE);
686
687 //fprintf(stderr,"Shrinking vector to: %d\n",newSlots);
688
689 // The goofy looking new below prevents operator new from getting called
690 // unnecessarily. This is severall times faster than allocating all of
691 // the slots as objects and then calling the assignment operator on them
692 // when they actually get used.
693 //
694 T *newVector=(T *)(new uint8[newSlots * sizeof(T)]);
695
696 if (Vector_ != NULL) // Vector_ better not be NULL!
697 memcpy(newVector,Vector_,Entries_*sizeof(T));
698
699 delete[]((uint8 *)Vector_); // Get rid of the old vector without calling
700 // destructors
701
702 Vector_=newVector;
703 Slots_=newSlots;
704
705 return(TRUE);
706}
707
708
709#endif
#define NULL
Definition BaseType.h:92
#define TRUE
Definition BaseType.h:109
#define FALSE
Definition BaseType.h:113
char bit8
Definition wstypes.h:61
#define IN
Definition wstypes.h:57
#define OUT
Definition wstypes.h:58
signed long sint32
Definition bittype.h:51
unsigned char uint8
Definition bittype.h:44
void add(float *sum, float *addend)
ArrayList< T > & operator=(IN ArrayList< T > &other)
bit8 addTail(IN T &node)
bit8 get(OUT T &node, sint32 pos) RO
bit8 getTail(OUT T &node) RO
bit8 replace(IN T &node, sint32 pos)
bit8 getPointer(OUT T **node, sint32 pos) RO
sint32 length(void) RO
bit8 addHead(IN T &node)
bit8 removeHead(OUT T &node)
void print(FILE *out)
bit8 getHead(OUT T &node) RO
bit8 setSize(sint32 newsize, IN T &filler)
void clear(void)
ArrayList(ArrayList< T > &other)
bit8 addSortedAsc(IN T &node)
bit8 remove(sint32 pos)
bit8 add(IN T &node, sint32 pos)
bit8 removeTail(OUT T &node)
bit8 addSortedDes(IN T &node)
bit8 remove(OUT T &node, sint32 pos)
#define RO
Definition wstypes.h:92