LCOV - code coverage report
Current view: top level - usr/include/irrlicht - irrArray.h (source / functions) Hit Total Coverage
Test: report Lines: 65 85 76.5 %
Date: 2015-07-11 18:23:49 Functions: 62 93 66.7 %

          Line data    Source code
       1             : // Copyright (C) 2002-2012 Nikolaus Gebhardt
       2             : // This file is part of the "Irrlicht Engine" and the "irrXML" project.
       3             : // For conditions of distribution and use, see copyright notice in irrlicht.h and irrXML.h
       4             : 
       5             : #ifndef __IRR_ARRAY_H_INCLUDED__
       6             : #define __IRR_ARRAY_H_INCLUDED__
       7             : 
       8             : #include "irrTypes.h"
       9             : #include "heapsort.h"
      10             : #include "irrAllocator.h"
      11             : #include "irrMath.h"
      12             : 
      13             : namespace irr
      14             : {
      15             : namespace core
      16             : {
      17             : 
      18             : //! Self reallocating template array (like stl vector) with additional features.
      19             : /** Some features are: Heap sorting, binary search methods, easier debugging.
      20             : */
      21             : template <class T, typename TAlloc = irrAllocator<T> >
      22             : class array
      23             : {
      24             : 
      25             : public:
      26             : 
      27             :         //! Default constructor for empty array.
      28     1129799 :         array()
      29             :                 : data(0), allocated(0), used(0),
      30     1129799 :                         strategy(ALLOC_STRATEGY_DOUBLE), free_when_destroyed(true), is_sorted(true)
      31             :         {
      32     1129799 :         }
      33             : 
      34             : 
      35             :         //! Constructs an array and allocates an initial chunk of memory.
      36             :         /** \param start_count Amount of elements to pre-allocate. */
      37             :         array(u32 start_count)
      38             :       : data(0), allocated(0), used(0),
      39             :         strategy(ALLOC_STRATEGY_DOUBLE), free_when_destroyed(true), is_sorted(true)
      40             :         {
      41             :                 reallocate(start_count);
      42             :         }
      43             : 
      44             : 
      45             :         //! Copy constructor
      46             :         array(const array<T, TAlloc>& other) : data(0)
      47             :         {
      48             :                 *this = other;
      49             :         }
      50             : 
      51             : 
      52             :         //! Destructor.
      53             :         /** Frees allocated memory, if set_free_when_destroyed was not set to
      54             :         false by the user before. */
      55     1137385 :         ~array()
      56             :         {
      57     1137385 :                 clear();
      58     1137385 :         }
      59             : 
      60             : 
      61             :         //! Reallocates the array, make it bigger or smaller.
      62             :         /** \param new_size New size of array.
      63             :         \param canShrink Specifies whether the array is reallocated even if
      64             :         enough space is available. Setting this flag to false can speed up
      65             :         array usage, but may use more memory than required by the data.
      66             :         */
      67     1190428 :         void reallocate(u32 new_size, bool canShrink=true)
      68             :         {
      69     1190428 :                 if (allocated==new_size)
      70           0 :                         return;
      71     1190428 :                 if (!canShrink && (new_size < allocated))
      72           0 :                         return;
      73             : 
      74     1190428 :                 T* old_data = data;
      75             : 
      76     1190428 :                 data = allocator.allocate(new_size); //new T[new_size];
      77     1190428 :                 allocated = new_size;
      78             : 
      79             :                 // copy old data
      80     1190428 :                 s32 end = used < new_size ? used : new_size;
      81             : 
      82    16722804 :                 for (s32 i=0; i<end; ++i)
      83             :                 {
      84             :                         // data[i] = old_data[i];
      85    15532376 :                         allocator.construct(&data[i], old_data[i]);
      86             :                 }
      87             : 
      88             :                 // destruct old data
      89    16722804 :                 for (u32 j=0; j<used; ++j)
      90    15532376 :                         allocator.destruct(&old_data[j]);
      91             : 
      92     1190428 :                 if (allocated < used)
      93           0 :                         used = allocated;
      94             : 
      95     1190428 :                 allocator.deallocate(old_data); //delete [] old_data;
      96             :         }
      97             : 
      98             : 
      99             :         //! set a new allocation strategy
     100             :         /** if the maximum size of the array is unknown, you can define how big the
     101             :         allocation should happen.
     102             :         \param newStrategy New strategy to apply to this array. */
     103             :         void setAllocStrategy ( eAllocStrategy newStrategy = ALLOC_STRATEGY_DOUBLE )
     104             :         {
     105             :                 strategy = newStrategy;
     106             :         }
     107             : 
     108             : 
     109             :         //! Adds an element at back of array.
     110             :         /** If the array is too small to add this new element it is made bigger.
     111             :         \param element: Element to add at the back of the array. */
     112    42486392 :         void push_back(const T& element)
     113             :         {
     114    42486392 :                 insert(element, used);
     115    42486392 :         }
     116             : 
     117             : 
     118             :         //! Adds an element at the front of the array.
     119             :         /** If the array is to small to add this new element, the array is
     120             :         made bigger. Please note that this is slow, because the whole array
     121             :         needs to be copied for this.
     122             :         \param element Element to add at the back of the array. */
     123             :         void push_front(const T& element)
     124             :         {
     125             :                 insert(element);
     126             :         }
     127             : 
     128             : 
     129             :         //! Insert item into array at specified position.
     130             :         /** Please use this only if you know what you are doing (possible
     131             :         performance loss). The preferred method of adding elements should be
     132             :         push_back().
     133             :         \param element: Element to be inserted
     134             :         \param index: Where position to insert the new element. */
     135    43205252 :         void insert(const T& element, u32 index=0)
     136             :         {
     137             :                 _IRR_DEBUG_BREAK_IF(index>used) // access violation
     138             : 
     139    43205252 :                 if (used + 1 > allocated)
     140             :                 {
     141             :                         // this doesn't work if the element is in the same
     142             :                         // array. So we'll copy the element first to be sure
     143             :                         // we'll get no data corruption
     144      135768 :                         const T e(element);
     145             : 
     146             :                         // increase data block
     147             :                         u32 newAlloc;
     148      131082 :                         switch ( strategy )
     149             :                         {
     150             :                                 case ALLOC_STRATEGY_DOUBLE:
     151      262164 :                                         newAlloc = used + 1 + (allocated < 500 ?
     152      131082 :                                                         (allocated < 5 ? 5 : used) : used >> 2);
     153      131082 :                                         break;
     154             :                                 default:
     155             :                                 case ALLOC_STRATEGY_SAFE:
     156           0 :                                         newAlloc = used + 1;
     157           0 :                                         break;
     158             :                         }
     159      131082 :                         reallocate( newAlloc);
     160             : 
     161             :                         // move array content and construct new element
     162             :                         // first move end one up
     163      131082 :                         for (u32 i=used; i>index; --i)
     164             :                         {
     165           0 :                                 if (i<used)
     166           0 :                                         allocator.destruct(&data[i]);
     167           0 :                                 allocator.construct(&data[i], data[i-1]); // data[i] = data[i-1];
     168             :                         }
     169             :                         // then add new element
     170      131082 :                         if (used > index)
     171           0 :                                 allocator.destruct(&data[index]);
     172      131082 :                         allocator.construct(&data[index], e); // data[index] = e;
     173             :                 }
     174             :                 else
     175             :                 {
     176             :                         // element inserted not at end
     177    43074170 :                         if ( used > index )
     178             :                         {
     179             :                                 // create one new element at the end
     180           0 :                                 allocator.construct(&data[used], data[used-1]);
     181             : 
     182             :                                 // move the rest of the array content
     183           0 :                                 for (u32 i=used-1; i>index; --i)
     184             :                                 {
     185           0 :                                         data[i] = data[i-1];
     186             :                                 }
     187             :                                 // insert the new element
     188           0 :                                 data[index] = element;
     189             :                         }
     190             :                         else
     191             :                         {
     192             :                                 // insert the new element to the end
     193    43074170 :                                 allocator.construct(&data[index], element);
     194             :                         }
     195             :                 }
     196             :                 // set to false as we don't know if we have the comparison operators
     197    43205245 :                 is_sorted = false;
     198    43205245 :                 ++used;
     199    43205245 :         }
     200             : 
     201             : 
     202             :         //! Clears the array and deletes all allocated memory.
     203     1137385 :         void clear()
     204             :         {
     205     1137385 :                 if (free_when_destroyed)
     206             :                 {
     207    44319583 :                         for (u32 i=0; i<used; ++i)
     208    43182198 :                                 allocator.destruct(&data[i]);
     209             : 
     210     1137385 :                         allocator.deallocate(data); // delete [] data;
     211             :                 }
     212     1137385 :                 data = 0;
     213     1137385 :                 used = 0;
     214     1137385 :                 allocated = 0;
     215     1137385 :                 is_sorted = true;
     216     1137385 :         }
     217             : 
     218             : 
     219             :         //! Sets pointer to new array, using this as new workspace.
     220             :         /** Make sure that set_free_when_destroyed is used properly.
     221             :         \param newPointer: Pointer to new array of elements.
     222             :         \param size: Size of the new array.
     223             :         \param _is_sorted Flag which tells whether the new array is already
     224             :         sorted.
     225             :         \param _free_when_destroyed Sets whether the new memory area shall be
     226             :         freed by the array upon destruction, or if this will be up to the user
     227             :         application. */
     228             :         void set_pointer(T* newPointer, u32 size, bool _is_sorted=false, bool _free_when_destroyed=true)
     229             :         {
     230             :                 clear();
     231             :                 data = newPointer;
     232             :                 allocated = size;
     233             :                 used = size;
     234             :                 is_sorted = _is_sorted;
     235             :                 free_when_destroyed=_free_when_destroyed;
     236             :         }
     237             : 
     238             : 
     239             :         //! Sets if the array should delete the memory it uses upon destruction.
     240             :         /** Also clear and set_pointer will only delete the (original) memory
     241             :         area if this flag is set to true, which is also the default. The
     242             :         methods reallocate, set_used, push_back, push_front, insert, and erase
     243             :         will still try to deallocate the original memory, which might cause
     244             :         troubles depending on the intended use of the memory area.
     245             :         \param f If true, the array frees the allocated memory in its
     246             :         destructor, otherwise not. The default is true. */
     247             :         void set_free_when_destroyed(bool f)
     248             :         {
     249             :                 free_when_destroyed = f;
     250             :         }
     251             : 
     252             : 
     253             :         //! Sets the size of the array and allocates new elements if necessary.
     254             :         /** Please note: This is only secure when using it with simple types,
     255             :         because no default constructor will be called for the added elements.
     256             :         \param usedNow Amount of elements now used. */
     257           2 :         void set_used(u32 usedNow)
     258             :         {
     259           2 :                 if (allocated < usedNow)
     260           2 :                         reallocate(usedNow);
     261             : 
     262           2 :                 used = usedNow;
     263           2 :         }
     264             : 
     265             : 
     266             :         //! Assignment operator
     267             :         const array<T, TAlloc>& operator=(const array<T, TAlloc>& other)
     268             :         {
     269             :                 if (this == &other)
     270             :                         return *this;
     271             :                 strategy = other.strategy;
     272             : 
     273             :                 if (data)
     274             :                         clear();
     275             : 
     276             :                 //if (allocated < other.allocated)
     277             :                 if (other.allocated == 0)
     278             :                         data = 0;
     279             :                 else
     280             :                         data = allocator.allocate(other.allocated); // new T[other.allocated];
     281             : 
     282             :                 used = other.used;
     283             :                 free_when_destroyed = true;
     284             :                 is_sorted = other.is_sorted;
     285             :                 allocated = other.allocated;
     286             : 
     287             :                 for (u32 i=0; i<other.used; ++i)
     288             :                         allocator.construct(&data[i], other.data[i]); // data[i] = other.data[i];
     289             : 
     290             :                 return *this;
     291             :         }
     292             : 
     293             : 
     294             :         //! Equality operator
     295             :         bool operator == (const array<T, TAlloc>& other) const
     296             :         {
     297             :                 if (used != other.used)
     298             :                         return false;
     299             : 
     300             :                 for (u32 i=0; i<other.used; ++i)
     301             :                         if (data[i] != other[i])
     302             :                                 return false;
     303             :                 return true;
     304             :         }
     305             : 
     306             : 
     307             :         //! Inequality operator
     308             :         bool operator != (const array<T, TAlloc>& other) const
     309             :         {
     310             :                 return !(*this==other);
     311             :         }
     312             : 
     313             : 
     314             :         //! Direct access operator
     315    62875973 :         T& operator [](u32 index)
     316             :         {
     317             :                 _IRR_DEBUG_BREAK_IF(index>=used) // access violation
     318             : 
     319    62875973 :                 return data[index];
     320             :         }
     321             : 
     322             : 
     323             :         //! Direct const access operator
     324     5240388 :         const T& operator [](u32 index) const
     325             :         {
     326             :                 _IRR_DEBUG_BREAK_IF(index>=used) // access violation
     327             : 
     328     5240388 :                 return data[index];
     329             :         }
     330             : 
     331             : 
     332             :         //! Gets last element.
     333             :         T& getLast()
     334             :         {
     335             :                 _IRR_DEBUG_BREAK_IF(!used) // access violation
     336             : 
     337             :                 return data[used-1];
     338             :         }
     339             : 
     340             : 
     341             :         //! Gets last element
     342             :         const T& getLast() const
     343             :         {
     344             :                 _IRR_DEBUG_BREAK_IF(!used) // access violation
     345             : 
     346             :                 return data[used-1];
     347             :         }
     348             : 
     349             : 
     350             :         //! Gets a pointer to the array.
     351             :         /** \return Pointer to the array. */
     352     2739834 :         T* pointer()
     353             :         {
     354     2739834 :                 return data;
     355             :         }
     356             : 
     357             : 
     358             :         //! Gets a const pointer to the array.
     359             :         /** \return Pointer to the array. */
     360     2912336 :         const T* const_pointer() const
     361             :         {
     362     2912336 :                 return data;
     363             :         }
     364             : 
     365             : 
     366             :         //! Get number of occupied elements of the array.
     367             :         /** \return Size of elements in the array which are actually occupied. */
     368    27087578 :         u32 size() const
     369             :         {
     370    27087578 :                 return used;
     371             :         }
     372             : 
     373             : 
     374             :         //! Get amount of memory allocated.
     375             :         /** \return Amount of memory allocated. The amount of bytes
     376             :         allocated would be allocated_size() * sizeof(ElementTypeUsed); */
     377             :         u32 allocated_size() const
     378             :         {
     379             :                 return allocated;
     380             :         }
     381             : 
     382             : 
     383             :         //! Check if array is empty.
     384             :         /** \return True if the array is empty false if not. */
     385      694050 :         bool empty() const
     386             :         {
     387      694050 :                 return used == 0;
     388             :         }
     389             : 
     390             : 
     391             :         //! Sorts the array using heapsort.
     392             :         /** There is no additional memory waste and the algorithm performs
     393             :         O(n*log n) in worst case. */
     394             :         void sort()
     395             :         {
     396             :                 if (!is_sorted && used>1)
     397             :                         heapsort(data, used);
     398             :                 is_sorted = true;
     399             :         }
     400             : 
     401             : 
     402             :         //! Performs a binary search for an element, returns -1 if not found.
     403             :         /** The array will be sorted before the binary search if it is not
     404             :         already sorted. Caution is advised! Be careful not to call this on
     405             :         unsorted const arrays, or the slower method will be used.
     406             :         \param element Element to search for.
     407             :         \return Position of the searched element if it was found,
     408             :         otherwise -1 is returned. */
     409             :         s32 binary_search(const T& element)
     410             :         {
     411             :                 sort();
     412             :                 return binary_search(element, 0, used-1);
     413             :         }
     414             : 
     415             : 
     416             :         //! Performs a binary search for an element if possible, returns -1 if not found.
     417             :         /** This method is for const arrays and so cannot call sort(), if the array is
     418             :         not sorted then linear_search will be used instead. Potentially very slow!
     419             :         \param element Element to search for.
     420             :         \return Position of the searched element if it was found,
     421             :         otherwise -1 is returned. */
     422             :         s32 binary_search(const T& element) const
     423             :         {
     424             :                 if (is_sorted)
     425             :                         return binary_search(element, 0, used-1);
     426             :                 else
     427             :                         return linear_search(element);
     428             :         }
     429             : 
     430             : 
     431             :         //! Performs a binary search for an element, returns -1 if not found.
     432             :         /** \param element: Element to search for.
     433             :         \param left First left index
     434             :         \param right Last right index.
     435             :         \return Position of the searched element if it was found, otherwise -1
     436             :         is returned. */
     437             :         s32 binary_search(const T& element, s32 left, s32 right) const
     438             :         {
     439             :                 if (!used)
     440             :                         return -1;
     441             : 
     442             :                 s32 m;
     443             : 
     444             :                 do
     445             :                 {
     446             :                         m = (left+right)>>1;
     447             : 
     448             :                         if (element < data[m])
     449             :                                 right = m - 1;
     450             :                         else
     451             :                                 left = m + 1;
     452             : 
     453             :                 } while((element < data[m] || data[m] < element) && left<=right);
     454             :                 // this last line equals to:
     455             :                 // " while((element != array[m]) && left<=right);"
     456             :                 // but we only want to use the '<' operator.
     457             :                 // the same in next line, it is "(element == array[m])"
     458             : 
     459             : 
     460             :                 if (!(element < data[m]) && !(data[m] < element))
     461             :                         return m;
     462             : 
     463             :                 return -1;
     464             :         }
     465             : 
     466             : 
     467             :         //! Performs a binary search for an element, returns -1 if not found.
     468             :         //! it is used for searching a multiset
     469             :         /** The array will be sorted before the binary search if it is not
     470             :         already sorted.
     471             :         \param element  Element to search for.
     472             :         \param &last        return lastIndex of equal elements
     473             :         \return Position of the first searched element if it was found,
     474             :         otherwise -1 is returned. */
     475             :         s32 binary_search_multi(const T& element, s32 &last)
     476             :         {
     477             :                 sort();
     478             :                 s32 index = binary_search(element, 0, used-1);
     479             :                 if ( index < 0 )
     480             :                         return index;
     481             : 
     482             :                 // The search can be somewhere in the middle of the set
     483             :                 // look linear previous and past the index
     484             :                 last = index;
     485             : 
     486             :                 while ( index > 0 && !(element < data[index - 1]) && !(data[index - 1] < element) )
     487             :                 {
     488             :                         index -= 1;
     489             :                 }
     490             :                 // look linear up
     491             :                 while ( last < (s32) used - 1 && !(element < data[last + 1]) && !(data[last + 1] < element) )
     492             :                 {
     493             :                         last += 1;
     494             :                 }
     495             : 
     496             :                 return index;
     497             :         }
     498             : 
     499             : 
     500             :         //! Finds an element in linear time, which is very slow.
     501             :         /** Use binary_search for faster finding. Only works if ==operator is
     502             :         implemented.
     503             :         \param element Element to search for.
     504             :         \return Position of the searched element if it was found, otherwise -1
     505             :         is returned. */
     506             :         s32 linear_search(const T& element) const
     507             :         {
     508             :                 for (u32 i=0; i<used; ++i)
     509             :                         if (element == data[i])
     510             :                                 return (s32)i;
     511             : 
     512             :                 return -1;
     513             :         }
     514             : 
     515             : 
     516             :         //! Finds an element in linear time, which is very slow.
     517             :         /** Use binary_search for faster finding. Only works if ==operator is
     518             :         implemented.
     519             :         \param element: Element to search for.
     520             :         \return Position of the searched element if it was found, otherwise -1
     521             :         is returned. */
     522             :         s32 linear_reverse_search(const T& element) const
     523             :         {
     524             :                 for (s32 i=used-1; i>=0; --i)
     525             :                         if (data[i] == element)
     526             :                                 return i;
     527             : 
     528             :                 return -1;
     529             :         }
     530             : 
     531             : 
     532             :         //! Erases an element from the array.
     533             :         /** May be slow, because all elements following after the erased
     534             :         element have to be copied.
     535             :         \param index: Index of element to be erased. */
     536           0 :         void erase(u32 index)
     537             :         {
     538             :                 _IRR_DEBUG_BREAK_IF(index>=used) // access violation
     539             : 
     540           0 :                 for (u32 i=index+1; i<used; ++i)
     541             :                 {
     542           0 :                         allocator.destruct(&data[i-1]);
     543           0 :                         allocator.construct(&data[i-1], data[i]); // data[i-1] = data[i];
     544             :                 }
     545             : 
     546           0 :                 allocator.destruct(&data[used-1]);
     547             : 
     548           0 :                 --used;
     549           0 :         }
     550             : 
     551             : 
     552             :         //! Erases some elements from the array.
     553             :         /** May be slow, because all elements following after the erased
     554             :         element have to be copied.
     555             :         \param index: Index of the first element to be erased.
     556             :         \param count: Amount of elements to be erased. */
     557             :         void erase(u32 index, s32 count)
     558             :         {
     559             :                 if (index>=used || count<1)
     560             :                         return;
     561             :                 if (index+count>used)
     562             :                         count = used-index;
     563             : 
     564             :                 u32 i;
     565             :                 for (i=index; i<index+count; ++i)
     566             :                         allocator.destruct(&data[i]);
     567             : 
     568             :                 for (i=index+count; i<used; ++i)
     569             :                 {
     570             :                         if (i-count >= index+count)  // not already destructed before loop
     571             :                                 allocator.destruct(&data[i-count]);
     572             : 
     573             :                         allocator.construct(&data[i-count], data[i]); // data[i-count] = data[i];
     574             : 
     575             :                         if (i >= used-count) // those which are not overwritten
     576             :                                 allocator.destruct(&data[i]);
     577             :                 }
     578             : 
     579             :                 used-= count;
     580             :         }
     581             : 
     582             : 
     583             :         //! Sets if the array is sorted
     584             :         void set_sorted(bool _is_sorted)
     585             :         {
     586             :                 is_sorted = _is_sorted;
     587             :         }
     588             : 
     589             : 
     590             :         //! Swap the content of this array container with the content of another array
     591             :         /** Afterwards this object will contain the content of the other object and the other
     592             :         object will contain the content of this object.
     593             :         \param other Swap content with this object      */
     594             :         void swap(array<T, TAlloc>& other)
     595             :         {
     596             :                 core::swap(data, other.data);
     597             :                 core::swap(allocated, other.allocated);
     598             :                 core::swap(used, other.used);
     599             :                 core::swap(allocator, other.allocator); // memory is still released by the same allocator used for allocation
     600             :                 eAllocStrategy helper_strategy(strategy);       // can't use core::swap with bitfields
     601             :                 strategy = other.strategy;
     602             :                 other.strategy = helper_strategy;
     603             :                 bool helper_free_when_destroyed(free_when_destroyed);
     604             :                 free_when_destroyed = other.free_when_destroyed;
     605             :                 other.free_when_destroyed = helper_free_when_destroyed;
     606             :                 bool helper_is_sorted(is_sorted);
     607             :                 is_sorted = other.is_sorted;
     608             :                 other.is_sorted = helper_is_sorted;
     609             :         }
     610             : 
     611             : 
     612             : private:
     613             :         T* data;
     614             :         u32 allocated;
     615             :         u32 used;
     616             :         TAlloc allocator;
     617             :         eAllocStrategy strategy:4;
     618             :         bool free_when_destroyed:1;
     619             :         bool is_sorted:1;
     620             : };
     621             : 
     622             : 
     623             : } // end namespace core
     624             : } // end namespace irr
     625             : 
     626             : #endif
     627             : 

Generated by: LCOV version 1.11