CrystalSpace

Public API Reference

Main Page   Modules   Class Hierarchy   Alphabetical List   Compound List   File List   Compound Members   File Members   Related Pages  

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