| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775 |
- /*
- ** Command & Conquer Renegade(tm)
- ** Copyright 2025 Electronic Arts Inc.
- **
- ** This program is free software: you can redistribute it and/or modify
- ** it under the terms of the GNU General Public License as published by
- ** the Free Software Foundation, either version 3 of the License, or
- ** (at your option) any later version.
- **
- ** This program is distributed in the hope that it will be useful,
- ** but WITHOUT ANY WARRANTY; without even the implied warranty of
- ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- ** GNU General Public License for more details.
- **
- ** You should have received a copy of the GNU General Public License
- ** along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
- /****************************************************************************\
- * 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 *
- ******************************************************************************
- Project Name:
- File Name : arraylist.h
- Author : Neal Kettler
- Start Date : Jan 19, 1997
- Last Update : Jan 19, 1997
- ------------------------------------------------------------------------------
- Array implementation of a list. Note: There are some freaky C++ memory tricks
- going on here. If you think there's a leak, see me before changing it.
- The way this works is that it allocates an array to hold 'N' items on the
- first list add. It doesn't call the constructors for those 'N' items until
- necessary (for efficiency). When an item is added to a slot then a new
- class is constructed inside the array element using the placement new operator
- and the class's copy constructor. When elements are removed the destructor
- is then manually called on this memory location.
- All data added to the list is copied so feel free to delete/destroy/modify
- the original after an add.
- You _must_ have a good copy constructor for any classes that you use this template
- for! A good copy constructor is one that won't blindly duplicate pointers
- that don't belong to them, etc...
- \****************************************************************************/
- #ifndef ARRAYLIST_HEADER
- #define ARRAYLIST_HEADER
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <assert.h>
- #include <new.h>
- #include <math.h>
- #include "wstypes.h"
- //
- // Usage: ArrayList<int> TheList;
- //
- template <class T>
- class ArrayList
- {
- public:
- ArrayList();
- ArrayList(ArrayList<T> &other);
- ~ArrayList();
- // Remove all entries from the lsit
- void clear(void);
- // Add a node after the zero based 'pos'
- bit8 add(IN T &node,sint32 pos);
- bit8 addTail(IN T &node);
- bit8 addHead(IN T &node);
- bit8 addSortedAsc(IN T &node); // Ascending
- bit8 addSortedDes(IN T &node); // Descending
- /*bit8 addNumSortedAsc(IN T &node); // Ascending
- bit8 addNumSortedDes(IN T &node); // Descending*/
- bit8 addMany(OUT T *outarray, sint32 pos, sint32 howmany);
- // Remove a node
- bit8 remove(OUT T &node,sint32 pos);
- bit8 remove(sint32 pos);
- bit8 removeHead(OUT T &node);
- bit8 removeTail(OUT T &node);
- sint32 removeMany(OUT T *outarray, sint32 pos, sint32 howmany);
- // Replace one obj with another
- bit8 replace(IN T &node, sint32 pos);
- // Get a node without removing from the list
- bit8 get(OUT T &node,sint32 pos) RO;
- bit8 getHead(OUT T &node) RO;
- bit8 getTail(OUT T &node) RO;
- // Get a pointer to the interally managed copy (careful!)
- bit8 getPointer(OUT T **node,sint32 pos) RO;
- // Get the number of entries in the list
- sint32 length(void) RO;
- // UNSAFE! for classes, see note below!
- bit8 setSize(sint32 newsize, IN T &filler);
- // Print information on the list
- void print(FILE *out);
- // assignment operator
- ArrayList<T> &operator=(IN ArrayList<T> &other);
- private:
- sint32 _sortedLookup(IN T &target, int ascending);
- sint32 Entries_; // Number of entries
- sint32 Slots_; // Number of available slots
- T *Vector_; // The actual memory where the list is held
- enum
- {
- INITIAL_SIZE = 10
- };
- bit8 growVector(void); // Expand the number of slots
- bit8 shrinkVector(void); // Reduce the number of slots
- };
- //Create the empty list
- template <class T>
- ArrayList<T>::ArrayList()
- {
- Entries_=0;
- Slots_=0;
- Vector_=NULL;
- }
- // copy constructor
- template <class T>
- ArrayList<T>::ArrayList(ArrayList<T> &other)
- {
- Entries_=0;
- Slots_=0;
- Vector_=NULL;
- (*this)=other;
- }
- //Free all the memory...
- template <class T>
- ArrayList<T>::~ArrayList()
- {
- clear(); // Remove the entries & call destructors on them
- delete[]((uint8*)Vector_); // this will prevent the destructors from
- // gettting called on elements not
- // containing valid objects.
- //fprintf(stderr,"Arraylist destructor\n");
- }
- // assignment operator
- template <class T>
- ArrayList<T> &ArrayList<T>::operator=(IN ArrayList<T> &other)
- {
- T node;
- clear();
- for (int i=0; i<other.length(); i++)
- {
- other.get(node,i);
- addTail(node);
- }
- return(*this);
- }
- // Remove all the entries and free the memory
- template <class T>
- void ArrayList<T>::clear()
- {
- for (int i=0; i<Entries_; i++)
- {
- (Vector_+i)->~T(); // Call the destructor manually. Don't try this
- // at home kiddies!
- }
- Entries_=0;
- }
- // ************************* UNSAFE UNSAFE UNSAFE *************************
- // Note - Don't call this on any type with a constructor/destructor since this
- // is really dumb and just puts a new one of filler in. Well, it's kindof safe
- // just be careful.
- // It's fine for simple classes like ints though..
- //
- // Add/remove entries in a stupid manner...
- //
- // **************************************************************************
- template <class T>
- bit8 ArrayList<T>::setSize(sint32 newsize, IN T &filler)
- {
- int oldEntries=Entries_;
- Entries_ = newsize;
- if (newsize<0)
- return(false);
- // Grow the vector as much as we need to
- while (newsize > Slots_)
- growVector();
- // Create new objects in the blank holes
- for (int i=oldEntries; i<Entries_; i++)
- {
- // Now put the replacement object in there...
- new((void *)(Vector_+i)) T(filler); // Trust me, this isn't a memory leak
- }
- // If we're at 33% usage or less, shrink the vector
- if ((Entries_*3) <= Slots_) // don't do while, because I think shrink will never goto 0
- shrinkVector();
- return(true);
- }
- // When adding into a position, the new node goes at the zero based slot
- // specified by pos. All other nodes get moved one slot down.
- template <class T>
- bit8 ArrayList<T>::add(IN T &node,sint32 pos)
- {
- if (pos > Entries_) // You can only access one of the end of the vector
- pos=Entries_;
- if (pos >= Slots_) // If we're at the end, grow the list
- growVector();
- if (Entries_ >= Slots_) // enuff space?
- growVector();
- // If we are insering into the middle or front of the list we have to
- // slide the old objects forward.
- if (pos < Entries_) // If there are elements after the add point
- memmove(Vector_+pos+1,Vector_+pos,sizeof(T)*(Entries_-pos)); // move them forward
- //fprintf(stderr,"Placement new to %p\n",(Vector_+pos));
- // This uses the placement new operator. placement new allows us to
- // specify the memory address for the new object. In this case we
- // want it at the 'pos' index into our array.
- new((void *)(Vector_+pos)) T((T &)node); // Trust me, this isn't a memory leak
- Entries_++; // one new entry
- return(TRUE);
- }
- // Add to the first node, all others get shifted down one slot
- template <class T>
- bit8 ArrayList<T>::addHead(IN T &node)
- {
- return(add(node,0));
- }
- // Append to the end of the list
- template <class T>
- bit8 ArrayList<T>::addTail(IN T &node)
- {
- return(add(node,length()));
- }
- // addSortedX only works (properly) if evrerything else in the list is added
- // using addSorted.
- template <class T>
- bit8 ArrayList<T>::addSortedAsc(IN T &node)
- {
- sint32 pos = _sortedLookup(node, 1);
- return(add(node, pos));
- }
- // addSortedX only works (properly) if evrerything else in the list is added
- // using addSorted.
- template <class T>
- bit8 ArrayList<T>::addSortedDes(IN T &node)
- {
- sint32 pos = _sortedLookup(node, 0);
- return(add(node, pos));
- }
- // This is the binary search used by addSorted
- template <class T>
- sint32 ArrayList<T>::_sortedLookup(IN T &target, int ascending)
- {
- int low, mid, high;
- T* lowtarget;
- T* hightarget;
- T* midtarget;
- // Trivial cases
- if( Entries_ == 0 )
- return 0;
- low = 0;
- high = Entries_ - 1;
- while( 1 )
- {
- assert( low <= high );
- mid = low + (int)(floor(((double)high - (double)low) / (double)2));
- getPointer(&lowtarget, low);
- getPointer(&hightarget, high);
- getPointer(&midtarget, mid);
- // Exact match
- if( *midtarget == target ) return mid;
- // Single element
- if( high == low )
- {
- if( ascending )
- {
- if( target <= *lowtarget )
- return low;
- else
- return low + 1;
- }
- else
- {
- if( target <= *lowtarget )
- return low + 1;
- else
- return low;
- }
- }
- // Two elemsnts
- if( (high - low) == 1 )
- {
- if( ascending )
- {
- if( target <= *lowtarget )
- return low;
- else if( target <= *hightarget )
- return high;
- else
- return high + 1;
- }
- else
- {
- if( target <= *hightarget )
- return high + 1;
- else if( target <= *lowtarget )
- return high;
- else
- return low;
- }
- }
- // Sorry, try again...
- if( ascending )
- {
- if( target < *midtarget )
- high = mid;
- else
- low = mid;
- }
- else
- {
- if( target < *midtarget )
- low = mid;
- else
- high = mid;
- }
- }
- }
- /*// addNumSortedX works in much the same way as addSortedX, except that I needed
- // it for a very specific thing. I needed a list of strings numerically sorted,
- // not alphabetically sorted. Furthermore these strings were composed of numbers
- // delimited by underscores. In the interest of keeping it generic, these
- // functions take as args a node, a delimiting character, and a count of the
- // number of fields to include in a sort. If this is the list of strings:
- //
- // 55_100, 2_5, 23_32, 98_445, 2_48, 8_88, 2_3, 2_4
- //
- // An alphabetical sort is:
- //
- // 2_3, 2_4, 2_48, 2_5, 55_100, 8_88, 98_445
- //
- // But a numerical sort by calling addNumSortedAsc(<whatever>, "_", 2) will result in:
- //
- // 2_3, 2_4, 2_5, 2_48, 8_88, 55_100, 98_445
- //
- // Yes...now that you mention it I am on crack...
- //
- template <class T>
- bit8 ArrayList<T>::addNumSortedAsc(IN T &node, char delim, int fields)
- {
- sint32 pos = _numSortedLookup(node, delim, fields, 1);
- return(add(node, pos));
- }
- // See addNumSortedAsc comment above.
- template <class T>
- bit8 ArrayList<T>::addSortedDes(IN T &node, char delim, int fields)
- {
- sint32 pos = _sortedLookup(node, delim, fields, 0);
- return(add(node, pos));
- }
- // This is the binary search used by addSorted
- template <class T>
- sint32 ArrayList<T>::_numSortedLookup(IN T &target, char delim, int fields, int ascending)
- {
- int low, mid, high;
- T* lowtarget;
- T* hightarget;
- T* midtarget;
- // Trivial case
- if( Entries_ == 0 )
- return 0;
-
- low = 0;
- high = Entries_;
- while( 1 )
- {
- assert( low <= high );
- mid = low + (int)(floor(((double)high - (double)low) / (double)2));
- getPointer(&lowtarget, low);
- getPointer(&hightarget, high);
- getPointer(&midtarget, mid);
- // Exact match
- if( *midtarget == target ) return mid;
- // Single element
- if( high == low )
- {
- if( ascending )
- {
- if( target <= *lowtarget )
- return low;
- else
- return low + 1;
- }
- else
- {
- if( target <= *lowtarget )
- return low + 1;
- else
- return low;
- }
- }
- // Two elemsnts
- if( (high - low) == 1 )
- {
- if( ascending )
- {
- if( target <= *lowtarget )
- return low;
- else
- return high;
- }
- else
- {
- if( target <= *lowtarget )
- return high;
- else
- return low;
- }
- }
- // Sorry, try again...
- if( ascending )
- {
- if( target < *midtarget )
- high = mid;
- else
- low = mid;
- }
- else
- {
- if( target < *midtarget )
- low = mid;
- else
- high = mid;
- }
- }
- }*/
- //
- // Delete an item at this index and construct a new one in it's place
- //
- template <class T>
- bit8 ArrayList<T>::replace(IN T &node, sint32 pos)
- {
- if (Entries_==0)
- return(FALSE);
- if (pos<0)
- pos=0;
- if (pos >= Entries_)
- pos=Entries_-1;
-
- (Vector_+pos)->~T(); // Call the destructor manually. Don't try this
- // at home kiddies!
- // Now put the replacement object in there...
- new((void *)(Vector_+pos)) T(node); // Trust me, this isn't a memory leak
- return(TRUE);
- }
- // Remove at the zero based index specified by 'pos'. When removing from
- // a slot, all others get shifted up by one.
- template <class T>
- bit8 ArrayList<T>::remove(sint32 pos)
- {
- if (Entries_==0)
- return(FALSE);
- if (pos<0)
- pos=0;
- if (pos >= Entries_)
- pos=Entries_-1;
-
- (Vector_+pos)->~T(); // Call the destructor manually. Don't try this
- // at home kiddies!
- memmove(Vector_+pos,Vector_+pos+1,sizeof(T)*(Entries_-pos-1));
- Entries_--;
- // If we're at 33% usage or less, shrink the vector
- if ( (Entries_*3) <= Slots_)
- shrinkVector();
- return(TRUE);
- }
- // Remove at the zero based index specified by 'pos'. When removing from
- // a slot, all others get shifted up by one.
- template <class T>
- bit8 ArrayList<T>::remove(OUT T &node, sint32 pos)
- {
- bit8 retval;
- retval=get(node,pos);
- if (retval==FALSE)
- return(FALSE);
- return(remove(pos));
- }
- // Remove the first node of the list
- template <class T>
- bit8 ArrayList<T>::removeHead(OUT T &node)
- {
- return(remove(node,0));
- }
- // Remove the last node of the list
- template <class T>
- bit8 ArrayList<T>::removeTail(OUT T &node)
- {
- return(remove(node,Entries_-1));
- }
- // get a pointer to the internally managed object. Try and avoid this, but
- // sometimes efficiency requires it...
- // get a copy of an item
- template <class T>
- bit8 ArrayList<T>::getPointer(OUT T **node,sint32 pos) RO
- {
- if ((pos < 0)||(pos >= Entries_))
- return(FALSE);
- *node=&(Vector_[pos]);
- return(TRUE);
- }
-
- // get a copy of an item
- template <class T>
- bit8 ArrayList<T>::get(OUT T &node,sint32 pos) RO
- {
- if ((pos < 0)||(pos >= Entries_))
- return(FALSE);
- node=Vector_[pos];
- return(TRUE);
- }
- // get a copy of the first node of the list
- template <class T>
- bit8 ArrayList<T>::getHead(OUT T &node) RO
- {
- return(get(node,0));
- }
- // get a copy of the last node
- template <class T>
- bit8 ArrayList<T>::getTail(OUT T &node) RO
- {
- return(get(node,Entries_-1));
- }
- // just for debugging
- template <class T>
- void ArrayList<T>::print(FILE *out)
- {
- fprintf(out,"--------------------\n");
- //for (int i=0; i<Entries_; i++)
- // Vector_[i].print();
- fprintf(out,"Entries: %d Slots: %d sizeof(T): %d\n",Entries_,Slots_,
- sizeof(T));
- fprintf(out,"--------------------\n");
- }
- // Return the current length of the list
- template <class T>
- sint32 ArrayList<T>::length(void) RO
- {
- return(Entries_);
- }
- // Grow the vector by a factor of 2X
- template <class T>
- bit8 ArrayList<T>::growVector(void)
- {
- if (Entries_ < Slots_) // Don't grow until we're at 100% usage
- return(FALSE);
- int newSlots=Entries_*2;
- if(newSlots < INITIAL_SIZE)
- newSlots=INITIAL_SIZE;
- //fprintf(stderr,"Growing vector to: %d\n",newSlots);
- // The goofy looking new below prevents operator new from getting called
- // unnecessarily. This is severall times faster than allocating all of
- // the slots as objects and then calling the assignment operator on them
- // when they actually get used.
- //
- T *newVector=(T *)(new uint8[newSlots * sizeof(T)]);
- memset(newVector,0,newSlots * sizeof(T)); // zero just to be safe
- if (Vector_ != NULL)
- memcpy(newVector,Vector_,Entries_*sizeof(T));
- delete[]((uint8 *)Vector_); // Get rid of the old vector without calling
- // destructors
- Vector_=newVector;
- Slots_=newSlots;
- return(TRUE);
- }
- // Shrink the vector by a factor of 2X
- template <class T>
- bit8 ArrayList<T>::shrinkVector(void)
- {
- //fprintf(stderr,"Shrink called\n");
- // Don't need to shrink until usage goes below 33%
- if ( (Entries_*3) > Slots_)
- return(FALSE);
- int newSlots=Slots_/2;
- if(newSlots < INITIAL_SIZE) // never shrink past initial size
- newSlots=INITIAL_SIZE;
- if (newSlots >= Slots_) // don't need to shrink
- return(FALSE);
- //fprintf(stderr,"Shrinking vector to: %d\n",newSlots);
-
- // The goofy looking new below prevents operator new from getting called
- // unnecessarily. This is severall times faster than allocating all of
- // the slots as objects and then calling the assignment operator on them
- // when they actually get used.
- //
- T *newVector=(T *)(new uint8[newSlots * sizeof(T)]);
-
- if (Vector_ != NULL) // Vector_ better not be NULL!
- memcpy(newVector,Vector_,Entries_*sizeof(T));
- delete[]((uint8 *)Vector_); // Get rid of the old vector without calling
- // destructors
-
- Vector_=newVector;
- Slots_=newSlots;
-
- return(TRUE);
- }
- // Implementation was missing from source code. This function is based on the build
- // artifact DLL in the archive and similar functions in this header file.
- // Compiles, but not tested. LFeenanEA - January 27th 2025
- template <class T>
- sint32 ArrayList<T>::removeMany(OUT T *outarray, sint32 pos, sint32 howmany)
- {
- if (Entries_==0)
- return(FALSE);
- if (pos<0)
- pos=0;
- if (pos>=Entries_)
- pos=Entries_-1;
- if (pos+howmany>Entries_)
- assert(0);
- for (int i=0; i<howmany; i++)
- {
- if (outarray)
- outarray[i]=Vector_[pos+i];
- }
- memmove(Vector_+pos,Vector_+pos+howmany,sizeof(T)*(Entries_-pos-howmany));
- Entries_-=howmany;
- // If we're at 33% usage or less, shrink the vector
- if ((Entries_*3) <= Slots_) // don't do while, because I think shrink will never goto 0
- shrinkVector();
- return(howmany);
- }
- // Implementation was missing from source code. This function is based on the build
- // artifact DLL in the archive and similar functions in this header file.
- // Compiles, but not tested. LFeenanEA - January 27th 2025
- template <class T>
- bit8 ArrayList<T>::addMany(IN T *inarray, sint32 pos, sint32 howmany)
- {
- if (pos > Entries_) // You can only access one of the end of the vector
- pos=Entries_;
- // Grow the vector as much as we need to
- while (howmany+Entries_ >= Slots_)
- growVector();
- // If we are insering into the middle or front of the list we have to
- // slide the old objects forward.
- if (pos < Entries_) // If there are elements after the add point
- memmove(Vector_+pos+howmany,Vector_+pos,sizeof(T)*(Entries_-pos)); // move them forward
- // This uses the placement new operator. placement new allows us to
- // specify the memory address for the new object. In this case we
- // want it at the 'pos' index into our array.
- for (int i=0; i<howmany; i++)
- {
- new((void *)(Vector_+i)) T(inarray[i]); // Trust me, this isn't a memory leak
- }
- Entries_+=howmany;
- return(TRUE);
- }
- #endif
|