csutil/array.h
00001 /* 00002 Crystal Space Generic Array Template 00003 Copyright (C) 2003 by Matze Braun 00004 Copyright (C) 2003 by Jorrit Tyberghein 00005 Copyright (C) 2003,2004 by Eric Sunshine 00006 00007 This library is free software; you can redistribute it and/or 00008 modify it under the terms of the GNU Library General Public 00009 License as published by the Free Software Foundation; either 00010 version 2 of the License, or (at your option) any later version. 00011 00012 This library is distributed in the hope that it will be useful, 00013 but WITHOUT ANY WARRANTY; without even the implied warranty of 00014 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00015 Library General Public License for more details. 00016 00017 You should have received a copy of the GNU Library General Public 00018 License along with this library; if not, write to the Free 00019 Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 00020 */ 00021 #ifndef __CSUTIL_ARRAY_H__ 00022 #define __CSUTIL_ARRAY_H__ 00023 00024 // Hack: Work around problems caused by #defining 'new'. 00025 #if defined(CS_EXTENSIVE_MEMDEBUG) || defined(CS_MEMORY_TRACKER) 00026 # undef new 00027 #endif 00028 #include <new> 00029 00030 #if defined(CS_MEMORY_TRACKER) 00031 #include "csutil/memdebug.h" 00032 #include <typeinfo> 00033 #endif 00034 00038 template <class T1, class T2> 00039 class csOrdering 00040 { 00041 public: 00055 static int Compare(T1 const &r1, T2 const &r2) 00056 { 00057 if (r1 < r2) return -1; 00058 else if (r2 < r1) return 1; 00059 else return 0; 00060 } 00061 }; 00062 00063 // Define CSARRAY_INHIBIT_TYPED_KEYS if the compiler is too old or too buggy to 00064 // properly support templated functions within a templated class. When this is 00065 // defined, rather than using a properly typed "key" argument, search methods 00066 // fall back to dealing with opaque void* for the "key" argument. Note, 00067 // however, that this fact is completely hidden from the client; the client 00068 // simply creates csArrayCmp<> functors using correct types for the keys 00069 // regardless of whether the compiler actually supports this feature. (The 00070 // MSVC6 compiler, for example, does support templated functions within a 00071 // template class but crashes and burns horribly when a function pointer or 00072 // functor is thrown into the mix; thus this should be defined for MSVC6.) 00073 #if !defined(CSARRAY_INHIBIT_TYPED_KEYS) 00074 00084 template <class T, class K> 00085 class csArrayCmp 00086 { 00087 public: 00093 typedef int(*CF)(T const&, K const&); 00095 csArrayCmp(K const& k, CF c = DefaultCompare) : key(k), cmp(c) {} 00097 csArrayCmp(csArrayCmp const& o) : key(o.key), cmp(o.cmp) {} 00099 csArrayCmp& operator=(csArrayCmp const& o) 00100 { key = o.key; cmp = o.cmp; return *this; } 00109 int operator()(T const& r) const { return cmp(r, key); } 00111 operator CF() const { return cmp; } 00113 operator K const&() const { return key; } 00124 static int DefaultCompare(T const& r, K const& k) 00125 { return csOrdering<T,K>::Compare(r,k); } 00126 private: 00127 K key; 00128 CF cmp; 00129 }; 00130 00131 #define csArrayTemplate(K) template <class K> 00132 #define csArrayCmpDecl(T1,T2) csArrayCmp<T1,T2> 00133 #define csArrayCmpInvoke(C,R) C(R) 00134 00135 #else // CSARRAY_INHIBIT_TYPED_KEYS 00136 00137 class csArrayCmpAbstract 00138 { 00139 public: 00140 typedef int(*CF)(void const*, void const*); 00141 virtual int operator()(void const*) const = 0; 00142 virtual operator CF() const = 0; 00143 }; 00144 00145 template <class T, class K> 00146 class csArrayCmp : public csArrayCmpAbstract 00147 { 00148 public: 00149 typedef int(*CFTyped)(T const&, K const&); 00150 csArrayCmp(K const& k, CFTyped c = DefaultCompare) : key(k), cmp(CF(c)) {} 00151 csArrayCmp(csArrayCmp const& o) : key(o.key), cmp(o.cmp) {} 00152 csArrayCmp& operator=(csArrayCmp const& o) 00153 { key = o.key; cmp = o.cmp; return *this; } 00154 virtual int operator()(void const* p) const { return cmp(p, &key); } 00155 virtual operator CF() const { return cmp; } 00156 operator K const&() const { return key; } 00157 static int DefaultCompare(T const& r, K const& k) 00158 { return csOrdering<T,K>::Compare(r,k); } 00159 private: 00160 K key; 00161 CF cmp; 00162 }; 00163 00164 #define csArrayTemplate(K) 00165 #define csArrayCmpDecl(T1,T2) csArrayCmpAbstract const& 00166 #define csArrayCmpInvoke(C,R) C(&(R)) 00167 00168 #endif // CSARRAY_INHIBIT_TYPED_KEYS 00169 00173 template <class T> 00174 class csArrayElementHandler 00175 { 00176 public: 00177 static void Construct (T* address, T const& src) 00178 { 00179 new (CS_STATIC_CAST(void*,address)) T(src); 00180 } 00181 00182 static void Destroy (T* address) 00183 { 00184 address->~T(); 00185 } 00186 00187 static void InitRegion (T* address, int count) 00188 { 00189 for (int i = 0 ; i < count ; i++) 00190 Construct (address + i, T ()); 00191 } 00192 }; 00193 00197 template <class T> 00198 class csArrayMemoryAllocator 00199 { 00200 public: 00201 static T* Alloc (int count) 00202 { 00203 return (T*)malloc (count * sizeof(T)); 00204 } 00205 00206 static void Free (T* mem) 00207 { 00208 free (mem); 00209 } 00210 00211 // The 'relevantcount' parameter should be the number of items 00212 // in the old array that are initialized. 00213 static T* Realloc (T* mem, int relevantcount, int oldcount, int newcount) 00214 { 00215 (void)relevantcount; (void)oldcount; 00216 return (T*)realloc (mem, newcount * sizeof(T)); 00217 } 00218 00219 // Move memory. 00220 static void MemMove (T* mem, int dest, int src, int count) 00221 { 00222 memmove (mem + dest, mem + src, count * sizeof(T)); 00223 } 00224 }; 00225 00234 template <class T, class ElementHandler = csArrayElementHandler<T> > 00235 class csSafeCopyArrayMemoryAllocator 00236 { 00237 public: 00238 static T* Alloc (int count) 00239 { 00240 return (T*)malloc (count * sizeof(T)); 00241 } 00242 00243 static void Free (T* mem) 00244 { 00245 free (mem); 00246 } 00247 00248 static T* Realloc (T* mem, int relevantcount, int oldcount, int newcount) 00249 { 00250 if (newcount <= oldcount) 00251 { 00252 // Realloc is safe. 00253 T* newmem = (T*)realloc (mem, newcount * sizeof (T)); 00254 CS_ASSERT (newmem == mem); 00255 return newmem; 00256 } 00257 00258 T* newmem = Alloc (newcount); 00259 int i; 00260 for (i = 0 ; i < relevantcount ; i++) 00261 { 00262 ElementHandler::Construct (newmem + i, mem[i]); 00263 ElementHandler::Destroy (mem + i); 00264 } 00265 Free (mem); 00266 return newmem; 00267 } 00268 00269 static void MemMove (T* mem, int dest, int src, int count) 00270 { 00271 int i; 00272 if (dest < src) 00273 { 00274 for (i = 0 ; i < count ; i++) 00275 { 00276 ElementHandler::Construct (mem + dest + i, mem[src + i]); 00277 ElementHandler::Destroy (mem + src + i); 00278 } 00279 } 00280 else 00281 { 00282 i = count; 00283 while (i > 0) 00284 { 00285 i--; 00286 ElementHandler::Construct (mem + dest + i, mem[src + i]); 00287 ElementHandler::Destroy (mem + src + i); 00288 } 00289 } 00290 } 00291 }; 00292 00293 00301 template <class T, 00302 class ElementHandler = csArrayElementHandler<T>, 00303 class MemoryAllocator = csArrayMemoryAllocator<T> > 00304 class csArray 00305 { 00306 private: 00307 int count; 00308 int capacity; 00309 int threshold; 00310 T* root; 00311 # ifdef CS_MEMORY_TRACKER 00312 MemTrackerInfo* mti; 00313 void UpdateMti (int dn, int curcapacity) 00314 { 00315 if (!mti) 00316 { 00317 if (!curcapacity) return; 00318 char buf[255]; 00319 sprintf (buf, "csArray<%s>", typeid (T).name()); 00320 mti = mtiRegisterAlloc (1 * sizeof (T), buf); 00321 if (!mti) return; 00322 curcapacity--; 00323 if (curcapacity) 00324 mtiUpdateAmount (mti, curcapacity, curcapacity * sizeof (T)); 00325 return; 00326 } 00327 mtiUpdateAmount (mti, dn, dn * sizeof (T)); 00328 } 00329 # endif 00330 00331 protected: 00336 void InitRegion (int start, int count) 00337 { 00338 ElementHandler::InitRegion (root+start, count); 00339 } 00340 00341 private: 00343 void CopyFrom (const csArray& source) 00344 { 00345 if (&source != this) 00346 { 00347 DeleteAll (); 00348 threshold = source.threshold; 00349 SetLengthUnsafe (source.Length ()); 00350 for (int i=0 ; i<source.Length() ; i++) 00351 ElementHandler::Construct (root + i, source[i]); 00352 } 00353 } 00354 00355 // Adjust internal capacity of this array. 00356 void AdjustCapacity (int n) 00357 { 00358 if (n > capacity || (capacity > threshold && n < capacity - threshold)) 00359 { 00360 n = ((n + threshold - 1) / threshold ) * threshold; 00361 if (root == 0) 00362 { 00363 root = MemoryAllocator::Alloc (n); 00364 # ifdef CS_MEMORY_TRACKER 00365 UpdateMti (n, n); 00366 # endif 00367 } 00368 else 00369 { 00370 root = MemoryAllocator::Realloc (root, count, capacity, n); 00371 # ifdef CS_MEMORY_TRACKER 00372 UpdateMti (n-capacity, n); 00373 # endif 00374 } 00375 capacity = n; 00376 } 00377 } 00378 00379 // Set array length. NOTE: Do not make this public since it does not 00380 // properly construct/destroy elements. To safely truncate the array, use 00381 // Truncate(). To safely set the capacity, use SetCapacity(). 00382 void SetLengthUnsafe (int n) 00383 { 00384 if (n > capacity) 00385 AdjustCapacity (n); 00386 count = n; 00387 } 00388 00389 public: 00401 static int DefaultCompare(T const& r1, T const& r2) 00402 { 00403 return csOrdering<T,T>::Compare(r1,r2); 00404 } 00405 00410 csArray (int icapacity = 0, int ithreshold = 0) 00411 { 00412 # ifdef CS_MEMORY_TRACKER 00413 mti = 0; 00414 # endif 00415 count = 0; 00416 capacity = (icapacity > 0 ? icapacity : 0); 00417 threshold = (ithreshold > 0 ? ithreshold : 16); 00418 if (capacity != 0) 00419 { 00420 root = MemoryAllocator::Alloc (capacity); 00421 # ifdef CS_MEMORY_TRACKER 00422 UpdateMti (capacity, capacity); 00423 # endif 00424 } 00425 else 00426 { 00427 root = 0; 00428 } 00429 } 00430 00431 ~csArray () 00432 { 00433 DeleteAll (); 00434 } 00435 00437 csArray (const csArray& source) 00438 { 00439 # ifdef CS_MEMORY_TRACKER 00440 mti = 0; 00441 # endif 00442 root = 0; 00443 capacity = 0; 00444 count = 0; 00445 CopyFrom (source); 00446 } 00447 00449 csArray<T,ElementHandler>& operator= (const csArray& other) 00450 { 00451 CopyFrom (other); 00452 return *this; 00453 } 00454 00456 int Length () const 00457 { 00458 return count; 00459 } 00460 00462 int Capacity () const 00463 { 00464 return capacity; 00465 } 00466 00473 void TransferTo (csArray& destination) 00474 { 00475 if (&destination != this) 00476 { 00477 destination.DeleteAll (); 00478 destination.root = root; 00479 destination.count = count; 00480 destination.capacity = capacity; 00481 destination.threshold = threshold; 00482 # ifdef CS_MEMORY_TRACKER 00483 destination.mti = mti; 00484 mti = 0; 00485 # endif 00486 root = 0; 00487 capacity = count = 0; 00488 } 00489 } 00490 00498 void SetLength (int n, T const& what) 00499 { 00500 if (n <= count) 00501 { 00502 Truncate (n); 00503 } 00504 else 00505 { 00506 int old_len = Length (); 00507 SetLengthUnsafe (n); 00508 for (int i = old_len ; i < n ; i++) 00509 ElementHandler::Construct (root + i, what); 00510 } 00511 } 00512 00514 void SetLength (int n) 00515 { 00516 if (n <= count) 00517 { 00518 Truncate (n); 00519 } 00520 else 00521 { 00522 int old_len = Length (); 00523 SetLengthUnsafe (n); 00524 ElementHandler::InitRegion (root + old_len, n-old_len); 00525 } 00526 } 00527 00529 T& Get (int n) 00530 { 00531 CS_ASSERT (n >= 0 && n < count); 00532 return root[n]; 00533 } 00534 00536 T const& Get (int n) const 00537 { 00538 CS_ASSERT (n >= 0 && n < count); 00539 return root[n]; 00540 } 00541 00546 T& GetExtend (int n) 00547 { 00548 CS_ASSERT (n >= 0); 00549 if (n >= count) 00550 SetLength (n+1); 00551 return root[n]; 00552 } 00553 00555 T& operator [] (int n) 00556 { 00557 return Get(n); 00558 } 00559 00561 T const& operator [] (int n) const 00562 { 00563 return Get(n); 00564 } 00565 00567 void Put (int n, T const& what) 00568 { 00569 CS_ASSERT (n >= 0); 00570 if (n >= count) 00571 SetLength (n+1); 00572 ElementHandler::Destroy (root + n); 00573 ElementHandler::Construct (root + n, what); 00574 } 00575 00579 csArrayTemplate(K) 00580 int FindKey (csArrayCmpDecl(T,K) comparekey) const 00581 { 00582 for (int i = 0 ; i < Length () ; i++) 00583 if (csArrayCmpInvoke(comparekey, root[i]) == 0) 00584 return i; 00585 return -1; 00586 } 00587 00589 int Push (T const& what) 00590 { 00591 if (((&what >= root) && (&what < root + Length())) && 00592 (capacity < count + 1)) 00593 { 00594 /* 00595 Special case: An element from this very array is pushed, and a 00596 reallocation is needed. This could cause the passed ref to the 00597 element to be pushed to be read from deleted memory. Work 00598 around this. 00599 */ 00600 int whatIndex = &what - root; 00601 SetLengthUnsafe (count + 1); 00602 ElementHandler::Construct (root + count - 1, root[whatIndex]); 00603 } 00604 else 00605 { 00606 SetLengthUnsafe (count + 1); 00607 ElementHandler::Construct (root + count - 1, what); 00608 } 00609 return count - 1; 00610 } 00611 00612 /* 00613 * Push a element onto the tail end of the array if not already present. 00614 * Returns index of newly pushed element or index of already present element. 00615 */ 00616 int PushSmart (T const& what) 00617 { 00618 int const n = Find (what); 00619 return (n < 0) ? Push (what) : n; 00620 } 00621 00623 T Pop () 00624 { 00625 CS_ASSERT (count > 0); 00626 T ret(root [count - 1]); 00627 ElementHandler::Destroy (root + count - 1); 00628 SetLengthUnsafe (count - 1); 00629 return ret; 00630 } 00631 00633 T const& Top () const 00634 { 00635 CS_ASSERT (count > 0); 00636 return root [count - 1]; 00637 } 00638 00640 T& Top () 00641 { 00642 CS_ASSERT (count > 0); 00643 return root [count - 1]; 00644 } 00645 00647 bool Insert (int n, T const& item) 00648 { 00649 if (n <= count) 00650 { 00651 SetLengthUnsafe (count + 1); // Increments 'count' as a side-effect. 00652 int const nmove = (count - n - 1); 00653 if (nmove > 0) 00654 MemoryAllocator::MemMove (root, n+1, n, nmove); 00655 ElementHandler::Construct (root + n, item); 00656 return true; 00657 } 00658 else 00659 return false; 00660 } 00661 00665 csArray<T> Section (int low, int high) const 00666 { 00667 CS_ASSERT (low >= 0 && high < count && high >= low); 00668 csArray<T> sect (high - low + 1); 00669 for (int i = low; i <= high; i++) sect.Push (root[i]); 00670 return sect; 00671 } 00672 00677 csArrayTemplate(K) 00678 int FindSortedKey (csArrayCmpDecl(T,K) comparekey, int* candidate = 0) const 00679 { 00680 int m = 0, l = 0, r = Length () - 1; 00681 while (l <= r) 00682 { 00683 m = (l + r) / 2; 00684 int cmp = csArrayCmpInvoke(comparekey, root[m]); 00685 if (cmp == 0) 00686 { 00687 if (candidate) *candidate = -1; 00688 return m; 00689 } 00690 else if (cmp < 0) 00691 l = m + 1; 00692 else 00693 r = m - 1; 00694 } 00695 if (candidate) *candidate = m; 00696 return -1; 00697 } 00698 00703 int InsertSorted (const T& item, 00704 int (*compare)(T const&, T const&) = DefaultCompare, 00705 int* equal_index = 0) 00706 { 00707 int m = 0, l = 0, r = Length () - 1; 00708 while (l <= r) 00709 { 00710 m = (l + r) / 2; 00711 int cmp = compare (root [m], item); 00712 00713 if (cmp == 0) 00714 { 00715 if (equal_index) *equal_index = m; 00716 Insert (++m, item); 00717 return m; 00718 } 00719 else if (cmp < 0) 00720 l = m + 1; 00721 else 00722 r = m - 1; 00723 } 00724 if (r == m) 00725 m++; 00726 if (equal_index) *equal_index = -1; 00727 Insert (m, item); 00728 return m; 00729 } 00730 00732 int Find (T const& which) const 00733 { 00734 for (int i = 0 ; i < Length () ; i++) 00735 if (root[i] == which) 00736 return i; 00737 return -1; 00738 } 00739 00746 int GetIndex (const T* which) const 00747 { 00748 return which-root; 00749 } 00750 00754 void Sort (int (*compare)(T const&, T const&) = DefaultCompare) 00755 { 00756 qsort (root, Length(), sizeof(T), 00757 (int (*)(void const*, void const*))compare); 00758 } 00759 00763 void DeleteAll () 00764 { 00765 if (root) 00766 { 00767 int i; 00768 for (i = 0 ; i < count ; i++) 00769 ElementHandler::Destroy (root + i); 00770 MemoryAllocator::Free (root); 00771 # ifdef CS_MEMORY_TRACKER 00772 UpdateMti (-capacity, 0); 00773 # endif 00774 root = 0; 00775 capacity = count = 0; 00776 } 00777 } 00778 00784 void Truncate (int n) 00785 { 00786 CS_ASSERT(n >= 0); 00787 CS_ASSERT(n <= count); 00788 if (n < count) 00789 { 00790 for (int i = n; i < count; i++) 00791 ElementHandler::Destroy (root + i); 00792 SetLengthUnsafe(n); 00793 } 00794 } 00795 00801 void Empty () 00802 { 00803 Truncate (0); 00804 } 00805 00812 void SetCapacity (int n) 00813 { 00814 if (n > Length ()) 00815 AdjustCapacity (n); 00816 } 00817 00823 void ShrinkBestFit () 00824 { 00825 if (count == 0) 00826 { 00827 DeleteAll (); 00828 } 00829 else if (count != capacity) 00830 { 00831 root = MemoryAllocator::Realloc (root, count, capacity, count); 00832 # ifdef CS_MEMORY_TRACKER 00833 UpdateMti (count-capacity, count); 00834 # endif 00835 capacity = count; 00836 } 00837 } 00838 00840 bool DeleteIndex (int n) 00841 { 00842 if (n >= 0 && n < count) 00843 { 00844 int const ncount = count - 1; 00845 int const nmove = ncount - n; 00846 ElementHandler::Destroy (root + n); 00847 if (nmove > 0) 00848 MemoryAllocator::MemMove (root, n, n+1, nmove); 00849 SetLengthUnsafe (ncount); 00850 return true; 00851 } 00852 else 00853 return false; 00854 } 00855 00860 void DeleteRange (int start, int end) 00861 { 00862 if (start >= count) return; 00863 if (end < 0) return; 00864 if (start < 0) start = 0; 00865 if (end >= count) end = count - 1; 00866 int i; 00867 for (i = start ; i <= end ; i++) 00868 ElementHandler::Destroy (root + i); 00869 00870 int const range_size = end - start + 1; 00871 int const ncount = count - range_size; 00872 int const nmove = count - end - 1; 00873 if (nmove > 0) 00874 MemoryAllocator::MemMove (root, start, start + range_size, nmove); 00875 SetLengthUnsafe (ncount); 00876 } 00877 00879 bool Delete (T const& item) 00880 { 00881 int const n = Find (item); 00882 if (n >= 0) 00883 return DeleteIndex (n); 00884 return false; 00885 } 00886 00888 class Iterator 00889 { 00890 public: 00892 Iterator(Iterator const& r) : 00893 currentelem(r.currentelem), array(r.array) {} 00894 00896 Iterator& operator=(Iterator const& r) 00897 { currentelem = r.currentelem; array = r.array; return *this; } 00898 00900 bool HasNext() 00901 { return currentelem < array.Length(); } 00902 00904 const T& Next() 00905 { return array.Get(currentelem++); } 00906 00908 void Reset() 00909 { currentelem = 0; } 00910 protected: 00911 Iterator(const csArray<T, ElementHandler>& newarray) 00912 : currentelem(0), array(newarray) 00913 { } 00914 friend class csArray<T, ElementHandler>; 00915 00916 private: 00917 int currentelem; 00918 const csArray<T, ElementHandler>& array; 00919 }; 00920 00922 Iterator GetIterator() const 00923 { return Iterator(*this); } 00924 }; 00925 00931 template <class T> 00932 class csSafeCopyArray 00933 : public csArray<T, 00934 csArrayElementHandler<T>, 00935 csSafeCopyArrayMemoryAllocator<T> > 00936 { 00937 public: 00942 csSafeCopyArray (int ilimit = 0, int ithreshold = 0) 00943 : csArray<T, csArrayElementHandler<T>, 00944 csSafeCopyArrayMemoryAllocator<T> > (ilimit, ithreshold) 00945 { 00946 } 00947 }; 00948 00949 #if defined(CS_EXTENSIVE_MEMDEBUG) || defined(CS_MEMORY_TRACKER) 00950 # define new CS_EXTENSIVE_MEMDEBUG_NEW 00951 #endif 00952 00953 #endif
Generated for Crystal Space by doxygen 1.2.18
