Main Page | Modules | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | File Members | Related Pages
array.h
Go to the documentation of this file.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 00028 #include "comparator.h" 00029 00030 // Hack: Work around problems caused by #defining 'new'. 00031 #if defined(CS_EXTENSIVE_MEMDEBUG) || defined(CS_MEMORY_TRACKER) 00032 # undef new 00033 #endif 00034 #include <new> 00035 00036 #if defined(CS_MEMORY_TRACKER) 00037 #include "csutil/memdebug.h" 00038 #include "csutil/snprintf.h" 00039 #include <typeinfo> 00040 #endif 00041 00045 // Define CSARRAY_INHIBIT_TYPED_KEYS if the compiler is too old or too buggy to 00046 // properly support templated functions within a templated class. When this is 00047 // defined, rather than using a properly typed "key" argument, search methods 00048 // fall back to dealing with opaque void* for the "key" argument. Note, 00049 // however, that this fact is completely hidden from the client; the client 00050 // simply creates csArrayCmp<> functors using correct types for the keys 00051 // regardless of whether the compiler actually supports this feature. (The 00052 // MSVC6 compiler, for example, does support templated functions within a 00053 // template class but crashes and burns horribly when a function pointer or 00054 // functor is thrown into the mix; thus this should be defined for MSVC6.) 00055 #if !defined(CSARRAY_INHIBIT_TYPED_KEYS) 00056 00066 template <class T, class K> 00067 class csArrayCmp 00068 { 00069 public: 00075 typedef int(*CF)(T const&, K const&); 00077 csArrayCmp(K const& k, CF c = DefaultCompare) : key(k), cmp(c) {} 00079 csArrayCmp(csArrayCmp const& o) : key(o.key), cmp(o.cmp) {} 00081 csArrayCmp& operator=(csArrayCmp const& o) 00082 { key = o.key; cmp = o.cmp; return *this; } 00091 int operator()(T const& r) const { return cmp(r, key); } 00093 operator CF() const { return cmp; } 00095 operator K const&() const { return key; } 00106 static int DefaultCompare(T const& r, K const& k) 00107 { return csComparator<T,K>::Compare(r,k); } 00108 private: 00109 K key; 00110 CF cmp; 00111 }; 00112 00113 #define csArrayTemplate(K) template <class K> 00114 #define csArrayCmpDecl(T1,T2) csArrayCmp<T1,T2> 00115 #define csArrayCmpInvoke(C,R) C(R) 00116 00117 #else // CSARRAY_INHIBIT_TYPED_KEYS 00118 00119 class csArrayCmpAbstract 00120 { 00121 public: 00122 typedef int(*CF)(void const*, void const*); 00123 virtual int operator()(void const*) const = 0; 00124 virtual operator CF() const = 0; 00125 }; 00126 00127 template <class T, class K> 00128 class csArrayCmp : public csArrayCmpAbstract 00129 { 00130 public: 00131 typedef int(*CFTyped)(T const&, K const&); 00132 csArrayCmp(K const& k, CFTyped c = DefaultCompare) : key(k), cmp(CF(c)) {} 00133 csArrayCmp(csArrayCmp const& o) : key(o.key), cmp(o.cmp) {} 00134 csArrayCmp& operator=(csArrayCmp const& o) 00135 { key = o.key; cmp = o.cmp; return *this; } 00136 virtual int operator()(void const* p) const { return cmp(p, &key); } 00137 virtual operator CF() const { return cmp; } 00138 operator K const&() const { return key; } 00139 static int DefaultCompare(T const& r, K const& k) 00140 { return csComparator<T,K>::Compare(r,k); } 00141 private: 00142 K key; 00143 CF cmp; 00144 }; 00145 00146 #define csArrayTemplate(K) 00147 #define csArrayCmpDecl(T1,T2) csArrayCmpAbstract const& 00148 #define csArrayCmpInvoke(C,R) C(&(R)) 00149 00150 #endif // CSARRAY_INHIBIT_TYPED_KEYS 00151 00155 template <class T> 00156 class csArrayElementHandler 00157 { 00158 public: 00159 static void Construct (T* address) 00160 { 00161 new (CS_STATIC_CAST(void*,address)) T(); 00162 } 00163 00164 static void Construct (T* address, T const& src) 00165 { 00166 new (CS_STATIC_CAST(void*,address)) T(src); 00167 } 00168 00169 static void Destroy (T* address) 00170 { 00171 address->~T(); 00172 } 00173 00174 static void InitRegion (T* address, size_t count) 00175 { 00176 for (size_t i = 0 ; i < count ; i++) 00177 Construct (address + i); 00178 } 00179 }; 00180 00184 template <class T> 00185 class csArrayMemoryAllocator 00186 { 00187 public: 00188 static T* Alloc (size_t count) 00189 { 00190 return (T*)malloc (count * sizeof(T)); 00191 } 00192 00193 static void Free (T* mem) 00194 { 00195 free (mem); 00196 } 00197 00198 // The 'relevantcount' parameter should be the number of items 00199 // in the old array that are initialized. 00200 static T* Realloc (T* mem, size_t relevantcount, size_t oldcount, 00201 size_t newcount) 00202 { 00203 (void)relevantcount; (void)oldcount; 00204 return (T*)realloc (mem, newcount * sizeof(T)); 00205 } 00206 00207 // Move memory. 00208 static void MemMove (T* mem, size_t dest, size_t src, size_t count) 00209 { 00210 memmove (mem + dest, mem + src, count * sizeof(T)); 00211 } 00212 }; 00213 00222 template <class T, class ElementHandler = csArrayElementHandler<T> > 00223 class csSafeCopyArrayMemoryAllocator 00224 { 00225 public: 00226 static T* Alloc (size_t count) 00227 { 00228 return (T*)malloc (count * sizeof(T)); 00229 } 00230 00231 static void Free (T* mem) 00232 { 00233 free (mem); 00234 } 00235 00236 static T* Realloc (T* mem, size_t relevantcount, size_t oldcount, 00237 size_t newcount) 00238 { 00239 if (newcount <= oldcount) 00240 { 00241 // Realloc is safe. 00242 T* newmem = (T*)realloc (mem, newcount * sizeof (T)); 00243 CS_ASSERT (newmem == mem); 00244 return newmem; 00245 } 00246 00247 T* newmem = Alloc (newcount); 00248 size_t i; 00249 for (i = 0 ; i < relevantcount ; i++) 00250 { 00251 ElementHandler::Construct (newmem + i, mem[i]); 00252 ElementHandler::Destroy (mem + i); 00253 } 00254 Free (mem); 00255 return newmem; 00256 } 00257 00258 static void MemMove (T* mem, size_t dest, size_t src, size_t count) 00259 { 00260 size_t i; 00261 if (dest < src) 00262 { 00263 for (i = 0 ; i < count ; i++) 00264 { 00265 ElementHandler::Construct (mem + dest + i, mem[src + i]); 00266 ElementHandler::Destroy (mem + src + i); 00267 } 00268 } 00269 else 00270 { 00271 i = count; 00272 while (i > 0) 00273 { 00274 i--; 00275 ElementHandler::Construct (mem + dest + i, mem[src + i]); 00276 ElementHandler::Destroy (mem + src + i); 00277 } 00278 } 00279 } 00280 }; 00281 00286 const size_t csArrayItemNotFound = (size_t)-1; 00287 00296 template <class T, 00297 class ElementHandler = csArrayElementHandler<T>, 00298 class MemoryAllocator = csArrayMemoryAllocator<T> > 00299 class csArray 00300 { 00301 private: 00302 size_t count; 00303 size_t capacity; 00304 size_t threshold; 00305 T* root; 00306 #ifdef CS_MEMORY_TRACKER 00307 csMemTrackerInfo* mti; 00308 void UpdateMti (int dn, int curcapacity) 00309 { 00310 if (!mti) 00311 { 00312 if (!curcapacity) return; 00313 char buf[1024]; 00314 cs_snprintf (buf, sizeof (buf), "csArray<%s>", typeid (T).name()); 00315 mti = mtiRegisterAlloc (1 * sizeof (T), buf); 00316 if (!mti) return; 00317 curcapacity--; 00318 if (curcapacity) 00319 mtiUpdateAmount (mti, curcapacity, curcapacity * sizeof (T)); 00320 return; 00321 } 00322 mtiUpdateAmount (mti, dn, dn * sizeof (T)); 00323 } 00324 #endif 00325 00326 protected: 00331 void InitRegion (size_t start, size_t count) 00332 { 00333 ElementHandler::InitRegion (root+start, count); 00334 } 00335 00336 private: 00338 void CopyFrom (const csArray& source) 00339 { 00340 if (&source != this) 00341 { 00342 DeleteAll (); 00343 threshold = source.threshold; 00344 SetSizeUnsafe (source.Length ()); 00345 for (size_t i=0 ; i<source.Length() ; i++) 00346 ElementHandler::Construct (root + i, source[i]); 00347 } 00348 } 00349 00351 void InternalSetCapacity (size_t n) 00352 { 00353 if (root == 0) 00354 { 00355 root = MemoryAllocator::Alloc (n); 00356 #ifdef CS_MEMORY_TRACKER 00357 UpdateMti (n, n); 00358 #endif 00359 } 00360 else 00361 { 00362 root = MemoryAllocator::Realloc (root, count, capacity, n); 00363 #ifdef CS_MEMORY_TRACKER 00364 UpdateMti (n-capacity, n); 00365 #endif 00366 } 00367 capacity = n; 00368 } 00369 00374 void AdjustCapacity (size_t n) 00375 { 00376 if (n > capacity || (capacity > threshold && n < capacity - threshold)) 00377 { 00378 InternalSetCapacity (((n + threshold - 1) / threshold ) * threshold); 00379 } 00380 } 00381 00388 void SetSizeUnsafe (size_t n) 00389 { 00390 if (n > capacity) 00391 AdjustCapacity (n); 00392 count = n; 00393 } 00394 00395 public: 00407 static int DefaultCompare(T const& r1, T const& r2) 00408 { 00409 return csComparator<T,T>::Compare(r1,r2); 00410 } 00411 00417 csArray (size_t in_capacity = 0, size_t in_threshold = 0) 00418 { 00419 #ifdef CS_MEMORY_TRACKER 00420 mti = 0; 00421 #endif 00422 count = 0; 00423 capacity = (in_capacity > 0 ? in_capacity : 0); 00424 threshold = (in_threshold > 0 ? in_threshold : 16); 00425 if (capacity != 0) 00426 { 00427 root = MemoryAllocator::Alloc (capacity); 00428 #ifdef CS_MEMORY_TRACKER 00429 UpdateMti (capacity, capacity); 00430 #endif 00431 } 00432 else 00433 { 00434 root = 0; 00435 } 00436 } 00437 00439 ~csArray () 00440 { 00441 DeleteAll (); 00442 } 00443 00445 csArray (const csArray& source) 00446 { 00447 #ifdef CS_MEMORY_TRACKER 00448 mti = 0; 00449 #endif 00450 root = 0; 00451 capacity = 0; 00452 count = 0; 00453 CopyFrom (source); 00454 } 00455 00457 csArray<T,ElementHandler>& operator= (const csArray& other) 00458 { 00459 CopyFrom (other); 00460 return *this; 00461 } 00462 00464 size_t GetSize () const 00465 { 00466 return count; 00467 } 00468 00473 size_t Length () const 00474 { 00475 return GetSize(); 00476 } 00477 00479 size_t Capacity () const 00480 { 00481 return capacity; 00482 } 00483 00490 void TransferTo (csArray& destination) 00491 { 00492 if (&destination != this) 00493 { 00494 destination.DeleteAll (); 00495 destination.root = root; 00496 destination.count = count; 00497 destination.capacity = capacity; 00498 destination.threshold = threshold; 00499 #ifdef CS_MEMORY_TRACKER 00500 destination.mti = mti; 00501 mti = 0; 00502 #endif 00503 root = 0; 00504 capacity = count = 0; 00505 } 00506 } 00507 00517 void SetSize (size_t n, T const& what) 00518 { 00519 if (n <= count) 00520 { 00521 Truncate (n); 00522 } 00523 else 00524 { 00525 size_t old_len = Length (); 00526 SetSizeUnsafe (n); 00527 for (size_t i = old_len ; i < n ; i++) 00528 ElementHandler::Construct (root + i, what); 00529 } 00530 } 00531 00539 void SetSize (size_t n) 00540 { 00541 if (n <= count) 00542 { 00543 Truncate (n); 00544 } 00545 else 00546 { 00547 size_t old_len = Length (); 00548 SetSizeUnsafe (n); 00549 ElementHandler::InitRegion (root + old_len, n-old_len); 00550 } 00551 } 00552 00558 void SetLength (size_t n, T const& what) { SetSize(n, what); } 00559 void SetLength (size_t n) { SetSize(n); } 00562 00563 T& Get (size_t n) 00564 { 00565 CS_ASSERT (n < count); 00566 return root[n]; 00567 } 00568 00570 T const& Get (size_t n) const 00571 { 00572 CS_ASSERT (n < count); 00573 return root[n]; 00574 } 00575 00581 T& GetExtend (size_t n) 00582 { 00583 if (n >= count) 00584 SetSize (n+1); 00585 return root[n]; 00586 } 00587 00589 T& operator [] (size_t n) 00590 { 00591 return Get(n); 00592 } 00593 00595 T const& operator [] (size_t n) const 00596 { 00597 return Get(n); 00598 } 00599 00601 void Put (size_t n, T const& what) 00602 { 00603 if (n >= count) 00604 SetSize (n+1); 00605 ElementHandler::Destroy (root + n); 00606 ElementHandler::Construct (root + n, what); 00607 } 00608 00616 csArrayTemplate(K) 00617 size_t FindKey (csArrayCmpDecl(T,K) comparekey) const 00618 { 00619 for (size_t i = 0 ; i < Length () ; i++) 00620 if (csArrayCmpInvoke(comparekey, root[i]) == 0) 00621 return i; 00622 return csArrayItemNotFound; 00623 } 00624 00629 size_t Push (T const& what) 00630 { 00631 if (((&what >= root) && (&what < root + Length())) && 00632 (capacity < count + 1)) 00633 { 00634 /* 00635 Special case: An element from this very array is pushed, and a 00636 reallocation is needed. This could cause the passed ref to the 00637 element to be pushed to be read from deleted memory. Work 00638 around this. 00639 */ 00640 size_t whatIndex = &what - root; 00641 SetSizeUnsafe (count + 1); 00642 ElementHandler::Construct (root + count - 1, root[whatIndex]); 00643 } 00644 else 00645 { 00646 SetSizeUnsafe (count + 1); 00647 ElementHandler::Construct (root + count - 1, what); 00648 } 00649 return count - 1; 00650 } 00651 00656 size_t PushSmart (T const& what) 00657 { 00658 size_t const n = Find (what); 00659 return (n == csArrayItemNotFound) ? Push (what) : n; 00660 } 00661 00663 T Pop () 00664 { 00665 CS_ASSERT (count > 0); 00666 T ret(root [count - 1]); 00667 ElementHandler::Destroy (root + count - 1); 00668 SetSizeUnsafe (count - 1); 00669 return ret; 00670 } 00671 00673 T const& Top () const 00674 { 00675 CS_ASSERT (count > 0); 00676 return root [count - 1]; 00677 } 00678 00680 T& Top () 00681 { 00682 CS_ASSERT (count > 0); 00683 return root [count - 1]; 00684 } 00685 00687 bool Insert (size_t n, T const& item) 00688 { 00689 if (n <= count) 00690 { 00691 SetSizeUnsafe (count + 1); // Increments 'count' as a side-effect. 00692 size_t const nmove = (count - n - 1); 00693 if (nmove > 0) 00694 MemoryAllocator::MemMove (root, n+1, n, nmove); 00695 ElementHandler::Construct (root + n, item); 00696 return true; 00697 } 00698 else 00699 return false; 00700 } 00701 00705 csArray<T> Section (size_t low, size_t high) const 00706 { 00707 CS_ASSERT (high < count && high >= low); 00708 csArray<T> sect (high - low + 1); 00709 for (size_t i = low; i <= high; i++) sect.Push (root[i]); 00710 return sect; 00711 } 00712 00718 csArrayTemplate(K) 00719 size_t FindSortedKey (csArrayCmpDecl(T,K) comparekey, 00720 size_t* candidate = 0) const 00721 { 00722 size_t m = 0, l = 0, r = Length (); 00723 while (l < r) 00724 { 00725 m = (l + r) / 2; 00726 int cmp = csArrayCmpInvoke(comparekey, root[m]); 00727 if (cmp == 0) 00728 { 00729 if (candidate) *candidate = csArrayItemNotFound; 00730 return m; 00731 } 00732 else if (cmp < 0) 00733 l = m + 1; 00734 else 00735 r = m; 00736 } 00737 if (candidate) *candidate = m; 00738 return csArrayItemNotFound; 00739 } 00740 00747 size_t InsertSorted (const T& item, 00748 int (*compare)(T const&, T const&) = DefaultCompare, 00749 size_t* equal_index = 0) 00750 { 00751 size_t m = 0, l = 0, r = Length (); 00752 while (l < r) 00753 { 00754 m = (l + r) / 2; 00755 int cmp = compare (root [m], item); 00756 00757 if (cmp == 0) 00758 { 00759 if (equal_index) *equal_index = m; 00760 Insert (++m, item); 00761 return m; 00762 } 00763 else if (cmp < 0) 00764 l = m + 1; 00765 else 00766 r = m; 00767 } 00768 if ((m + 1) == r) 00769 m++; 00770 if (equal_index) *equal_index = csArrayItemNotFound; 00771 Insert (m, item); 00772 return m; 00773 } 00774 00781 size_t Find (T const& which) const 00782 { 00783 for (size_t i = 0 ; i < Length () ; i++) 00784 if (root[i] == which) 00785 return i; 00786 return csArrayItemNotFound; 00787 } 00788 00790 size_t Contains(T const& which) const 00791 { return Find(which); } 00792 00799 size_t GetIndex (const T* which) const 00800 { 00801 CS_ASSERT (which >= root); 00802 CS_ASSERT (which < (root + count)); 00803 return which-root; 00804 } 00805 00809 void Sort (int (*compare)(T const&, T const&) = DefaultCompare) 00810 { 00811 qsort (root, Length(), sizeof(T), 00812 (int (*)(void const*, void const*))compare); 00813 } 00814 00818 void DeleteAll () 00819 { 00820 if (root) 00821 { 00822 size_t i; 00823 for (i = 0 ; i < count ; i++) 00824 ElementHandler::Destroy (root + i); 00825 MemoryAllocator::Free (root); 00826 # ifdef CS_MEMORY_TRACKER 00827 UpdateMti (-capacity, 0); 00828 # endif 00829 root = 0; 00830 capacity = count = 0; 00831 } 00832 } 00833 00845 void Truncate (size_t n) 00846 { 00847 CS_ASSERT(n <= count); 00848 if (n < count) 00849 { 00850 for (size_t i = n; i < count; i++) 00851 ElementHandler::Destroy (root + i); 00852 SetSizeUnsafe(n); 00853 } 00854 } 00855 00861 void Empty () 00862 { 00863 Truncate (0); 00864 } 00865 00871 bool IsEmpty() const 00872 { 00873 return GetSize() == 0; 00874 } 00875 00882 void SetCapacity (size_t n) 00883 { 00884 if (n > Length ()) 00885 InternalSetCapacity (n); 00886 } 00887 00893 void ShrinkBestFit () 00894 { 00895 if (count == 0) 00896 { 00897 DeleteAll (); 00898 } 00899 else if (count != capacity) 00900 { 00901 root = MemoryAllocator::Realloc (root, count, capacity, count); 00902 #ifdef CS_MEMORY_TRACKER 00903 UpdateMti (count-capacity, count); 00904 #endif 00905 capacity = count; 00906 } 00907 } 00908 00917 bool DeleteIndex (size_t n) 00918 { 00919 if (n < count) 00920 { 00921 size_t const ncount = count - 1; 00922 size_t const nmove = ncount - n; 00923 ElementHandler::Destroy (root + n); 00924 if (nmove > 0) 00925 MemoryAllocator::MemMove (root, n, n+1, nmove); 00926 SetSizeUnsafe (ncount); 00927 return true; 00928 } 00929 else 00930 return false; 00931 } 00932 00942 bool DeleteIndexFast (size_t n) 00943 { 00944 if (n < count) 00945 { 00946 size_t const ncount = count - 1; 00947 size_t const nmove = ncount - n; 00948 ElementHandler::Destroy (root + n); 00949 if (nmove > 0) 00950 MemoryAllocator::MemMove (root, n, ncount, 1); 00951 SetSizeUnsafe (ncount); 00952 return true; 00953 } 00954 else 00955 return false; 00956 } 00957 00962 void DeleteRange (size_t start, size_t end) 00963 { 00964 if (start >= count) return; 00965 // Treat 'csArrayItemNotFound' as invalid indices, do nothing. 00966 // @@@ Assert that? 00967 if (end == csArrayItemNotFound) return; 00968 if (start == csArrayItemNotFound) return;//start = 0; 00969 if (end >= count) end = count - 1; 00970 size_t i; 00971 for (i = start ; i <= end ; i++) 00972 ElementHandler::Destroy (root + i); 00973 00974 size_t const range_size = end - start + 1; 00975 size_t const ncount = count - range_size; 00976 size_t const nmove = count - end - 1; 00977 if (nmove > 0) 00978 MemoryAllocator::MemMove (root, start, start + range_size, nmove); 00979 SetSizeUnsafe (ncount); 00980 } 00981 00987 bool Delete (T const& item) 00988 { 00989 size_t const n = Find (item); 00990 if (n != csArrayItemNotFound) 00991 return DeleteIndex (n); 00992 return false; 00993 } 00994 01008 bool DeleteFast (T const& item) 01009 { 01010 size_t const n = Find (item); 01011 if (n != csArrayItemNotFound) 01012 return DeleteIndexFast (n); 01013 return false; 01014 } 01015 01017 class Iterator 01018 { 01019 public: 01021 Iterator(Iterator const& r) : 01022 currentelem(r.currentelem), array(r.array) {} 01023 01025 Iterator& operator=(Iterator const& r) 01026 { currentelem = r.currentelem; array = r.array; return *this; } 01027 01029 bool HasNext() 01030 { return currentelem < array.Length(); } 01031 01033 const T& Next() 01034 { return array.Get(currentelem++); } 01035 01037 void Reset() 01038 { currentelem = 0; } 01039 01040 protected: 01041 Iterator(const csArray<T, ElementHandler>& newarray) 01042 : currentelem(0), array(newarray) {} 01043 friend class csArray<T, ElementHandler>; 01044 01045 private: 01046 size_t currentelem; 01047 const csArray<T, ElementHandler>& array; 01048 }; 01049 01051 Iterator GetIterator() const 01052 { return Iterator(*this); } 01053 }; 01054 01060 template <class T> 01061 class csSafeCopyArray 01062 : public csArray<T, 01063 csArrayElementHandler<T>, 01064 csSafeCopyArrayMemoryAllocator<T> > 01065 { 01066 public: 01071 csSafeCopyArray (size_t limit = 0, size_t threshold = 0) 01072 : csArray<T, csArrayElementHandler<T>, 01073 csSafeCopyArrayMemoryAllocator<T> > (limit, threshold) 01074 { 01075 } 01076 }; 01077 01078 #if defined(CS_EXTENSIVE_MEMDEBUG) || defined(CS_MEMORY_TRACKER) 01079 # define new CS_EXTENSIVE_MEMDEBUG_NEW 01080 #endif 01081 01084 #endif
Generated for Crystal Space by doxygen 1.4.4