arraylist.h 17 KB

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