Tekkotsu Homepage
Demos
Overview
Downloads
Dev. Resources
Reference
Credits

ListMemBuf.h

Go to the documentation of this file.
00001 //-*-c++-*-
00002 #ifndef INCLUDED_ListMemBuf_h
00003 #define INCLUDED_ListMemBuf_h
00004 
00005 //! Provides some degree of dynamic allocation of a templated type from a buffer of set size.
00006 /*! Think of this as a self-contained mini-malloc...
00007     
00008     This is handy for classes which inhabit a shared memory region, where
00009     it's a bad idea to have pointers to other memory.  By instantiating one
00010     of these in your class, you can allocate space internally for up to
00011     MAX objects of type T_t.  ListMemBuf will worry about keeping track
00012     of which ones are in use or are free.
00013 
00014     Each time you request a entry to be created, the destructor will be
00015     called followed by the the defaul constructor before it is given to
00016     you, so the fields should be reliably 'fresh', not what was in the
00017     entry last time it was used.
00018 */
00019 template < class T_t, unsigned int MAX, class idx_t=unsigned short >
00020 class ListMemBuf {
00021   public:
00022   
00023   ListMemBuf(); //!<constructor
00024   ~ListMemBuf(); //!<destructor
00025   
00026   //!Allows outside access to storage type
00027   typedef T_t T;
00028   //!Allows outside access to number of entries
00029   static const unsigned int MAX_ENTRIES = MAX;
00030   //!Allows outside access to index type
00031   typedef idx_t index_t;
00032   
00033   static index_t getMaxCapacity() { return MAX_ENTRIES; } //!<returns the maximum number of objects which can be used at any given time
00034   index_t size() const { return cursize; } //!<returns the current number of objects in use
00035   index_t countf() const; //!< for debugging, should equal size
00036   index_t countb() const; //!< for debugging, should equal size
00037   bool empty() const { return cursize==0; } //!<returns true if no objects are in use
00038 
00039   // the funny '(T*)(void*)foo' double-casts below are to avoid gcc warnings
00040   // regarding 'dereferencing type-punned pointer will break strict-aliasing rules'
00041   
00042   inline T& operator[](unsigned int x) { return *(T*)(void*)(entries[x].data); } //!<allows direct access to elements - be careful, can access 'free' elements this way
00043   inline const T& operator[](unsigned int x) const { return *(const T*)(const void*)(entries[x].data); } //!<allows direct access to elements - be careful, can access 'free' elements this way
00044 
00045   inline index_t begin() const { return activeBegin; } //!<returns index of first used entry
00046   T& front() { return *(T*)(void*)(entries[activeBegin].data); } //!< returns reference to first used entry
00047   const T& front() const { return *(const T*)(const void*)(entries[activeBegin].data); } //!< returns const reference to first used entry
00048 
00049   inline index_t end() const { return (index_t)-1; } //!<returns the one-past-end index
00050   T& back() { return *(T*)(void*)(entries[activeBack].data); } //!<returns reference to last used entry
00051   const T& back() const { return *(const T*)(const void*)(entries[activeBack].data); } //!<returns const reference to last used entry
00052 
00053   index_t new_front(); //!<pushes a 'blank' entry on the front of the used list
00054   index_t push_front(const T& data) { index_t tmp=new_front(); if(tmp!=end()) operator[](tmp)=data; return tmp; } //!<pushes an entry on the front of the used chain and assigns @a data to it
00055   void pop_front(); //!<pops the front of the used chain
00056   void pop_front(T& ret) { ret=front(); pop_front(); } //!<pops the front of the chain into @a ret
00057 
00058   index_t new_back(); //!<pushes a 'blank' entry on the back of the used list
00059   index_t push_back(const T& data) { index_t tmp=new_back(); if(tmp!=end()) operator[](tmp)=data; return tmp; } //!<pushes an entry on the back of the used chain and assigns @a data to it
00060   void pop_back(); //!<pops the last of the used chain
00061   void pop_back(T& ret) { ret=back(); pop_back(); } //!<pops the last of the used chain into @a ret
00062 
00063   index_t new_before(index_t x); //!<inserts a 'blank' entry before element @a x in the used chain
00064   index_t push_before(index_t x, const T& data) { index_t tmp=new_before(x); if(tmp!=end()) operator[](tmp)=data; return tmp; } //!<inserts a 'blank' entry before element @a x in the used chain and assigns @a data to it
00065 
00066   index_t new_after(index_t x) { return new_before(next(x)); } //!<inserts a 'blank' entry after element @a x in the used chain
00067   index_t push_after(index_t x, const T& data) { index_t tmp=new_after(x); if(tmp!=end()) operator[](tmp)=data; return tmp; } //!<inserts a 'blank' entry after element @a x in the used chain and assigns @a data to it
00068 
00069   void erase(index_t x); //!<removes element @a x from the used chain
00070   void clear(); //!<frees all used entries
00071 
00072   void swap(index_t a, index_t b); //!<swaps the two entries' position in the list
00073 
00074   index_t next(index_t x) const { return x==end()?activeBegin:entries[x].next; } //!< returns the next used element following @a x
00075   index_t prev(index_t x) const { return x==end()?activeBack:entries[x].prev; } //!< returns the preceeding used element following @a x
00076  
00077   protected:
00078   index_t pop_free(); //!<removes an element from the front of the free list, returns its index
00079   void push_free(index_t x); //!<pushes @a x onto the back of the free list
00080   
00081   //!holds data about an entry in the free/used lists
00082   struct entry_t {
00083     //!constructor
00084     entry_t() : next(static_cast<index_t>(-1)), prev(static_cast<index_t>(-1)) {}
00085     double data[(sizeof(T)-1)/sizeof(double)+1]; //!<The data being stored, not actually an instantiation of T, but big enough to hold it.  (Funky array size is to ensure proper alignment of contents)
00086     index_t next; //!<The next element in the used or free chain
00087     index_t prev; //!<The previous element in the used chain, invalid if in the free chain
00088   };
00089   entry_t entries[MAX_ENTRIES==0?1:MAX_ENTRIES]; //!<the main block of data; must have at least 1 element due to limitation of older compilers
00090   index_t activeBegin; //!<beginning of used chain
00091   index_t activeBack; //!<end of used chain
00092   index_t freeBegin; //!<beginning of free chain
00093   index_t freeBack; //!<end of free chain
00094   index_t cursize; //!< current number of used elements
00095 };
00096 
00097 template < class T, unsigned int MAX, class index_t >
00098 ListMemBuf<T,MAX,index_t>::ListMemBuf()
00099   : activeBegin(end()), activeBack(end()), freeBegin(end()), freeBack(end()), cursize(0)
00100 {
00101   for(unsigned int x=0; x+1<MAX_ENTRIES; x++)
00102     entries[x].next=x+1;
00103   entries[MAX_ENTRIES-1].next=end();
00104   freeBegin=0;
00105   freeBack=MAX_ENTRIES-1;
00106 }
00107 
00108 template < class T, unsigned int MAX, class index_t >
00109 ListMemBuf<T,MAX,index_t>::~ListMemBuf() {
00110   clear();
00111 }
00112 
00113 template < class T, unsigned int MAX, class index_t>
00114 index_t
00115 ListMemBuf<T,MAX,index_t>::countf() const {
00116   int x=0;
00117   for(index_t c=begin(); c!=end(); c=next(c))
00118     x++;
00119   return x;
00120 }
00121 
00122 template < class T, unsigned int MAX, class index_t>
00123 index_t
00124 ListMemBuf<T,MAX,index_t>::countb() const {
00125   int x=0;
00126   for(index_t c=end(); c!=begin(); c=prev(c))
00127     x++;
00128   return x;
00129 }
00130 
00131 template < class T, unsigned int MAX, class index_t >
00132 index_t
00133 ListMemBuf<T,MAX,index_t>::new_front() {
00134   index_t tmp = pop_free();
00135   if(tmp==end())
00136     return end();
00137   entries[tmp].prev=end();
00138   entries[tmp].next=activeBegin;
00139   if(activeBegin!=end())
00140     entries[activeBegin].prev=tmp;
00141   else
00142     activeBack=tmp;
00143   activeBegin=tmp;
00144   return tmp;
00145 }
00146 
00147 template < class T, unsigned int MAX, class index_t >
00148 index_t
00149 ListMemBuf<T,MAX,index_t>::new_back() {
00150   index_t tmp = pop_free();
00151   if(tmp==end())
00152     return end();
00153   entries[tmp].prev=activeBack;
00154   entries[tmp].next=end();
00155   if(activeBack!=end())
00156     entries[activeBack].next=tmp;
00157   else
00158     activeBegin=tmp;
00159   activeBack=tmp;
00160   return tmp;
00161 }
00162 
00163 template < class T, unsigned int MAX, class index_t >
00164 index_t
00165 ListMemBuf<T,MAX,index_t>::new_before(index_t x) {
00166   if(x==end())
00167     return new_back();
00168   if(entries[x].prev==end())
00169     return new_front();
00170   index_t tmp = pop_free();
00171   if(tmp==end())
00172     return end();
00173   entries[tmp].next=x;
00174   entries[tmp].prev=entries[x].prev;
00175   entries[x].prev=tmp;
00176   entries[ entries[tmp].prev ].next = tmp;
00177   return tmp;
00178 }
00179 
00180 template < class T, unsigned int MAX, class index_t >
00181 void
00182 ListMemBuf<T,MAX,index_t>::pop_front() {
00183   index_t tmp = activeBegin;
00184   activeBegin = entries[activeBegin].next;
00185   if(activeBegin==end())
00186     activeBack=end();
00187   else
00188     entries[activeBegin].prev = end();
00189   push_free(tmp);
00190 }
00191 
00192 template < class T, unsigned int MAX, class index_t >
00193 void
00194 ListMemBuf<T,MAX,index_t>::pop_back() {
00195   index_t tmp = activeBack;
00196   activeBack = entries[activeBack].prev;
00197   if(activeBack==end())
00198     activeBegin=end();
00199   else
00200     entries[activeBack].next = end();
00201   push_free(tmp);
00202 }
00203 
00204 template < class T, unsigned int MAX, class index_t >
00205 void
00206 ListMemBuf<T,MAX,index_t>::erase(index_t x) {
00207   if(x==activeBegin) {
00208     pop_front();
00209     return;
00210   }
00211   if(x==activeBack) {
00212     pop_back();
00213     return;
00214   }
00215   entries[ entries[x].next ].prev = entries[x].prev;
00216   entries[ entries[x].prev ].next = entries[x].next;
00217   push_free(x);
00218 }
00219 
00220 template < class T, unsigned int MAX, class index_t >
00221 void
00222 ListMemBuf<T,MAX,index_t>::clear() {
00223   if(cursize!=0) {
00224     for(index_t it=activeBegin; it!=end(); it=entries[it].next)
00225       operator[](it).~T();
00226     if(freeBack==end())
00227       freeBegin=activeBegin;
00228     else
00229       entries[freeBack].next=activeBegin;
00230     freeBack=activeBack;
00231     activeBegin=activeBack=end();
00232   }
00233   cursize=0;
00234 }
00235 
00236 template < class T, unsigned int MAX, class index_t >
00237 void
00238 ListMemBuf<T,MAX,index_t>::swap(index_t a, index_t b) {
00239   if(a==b || a==end() || b==end())
00240     return;
00241   if(entries[a].prev==b) {
00242     entries[a].prev=entries[b].prev;
00243     entries[b].next=entries[a].next;
00244     entries[a].next=b;
00245     entries[b].prev=a;
00246     if(entries[a].prev!=end())
00247       entries[entries[a].prev].next=a;
00248     else
00249       activeBegin=a;
00250     if(entries[b].next!=end())
00251       entries[entries[b].next].prev=b;
00252     else
00253       activeBack=b;
00254   } else if(entries[a].next==b) {
00255     entries[a].next=entries[b].next;
00256     entries[b].prev=entries[a].prev;
00257     entries[a].prev=b;
00258     entries[b].next=a;
00259     if(entries[a].next!=end())
00260       entries[entries[a].next].prev=a;
00261     else
00262       activeBack=a;
00263     if(entries[b].prev!=end())
00264       entries[entries[b].prev].next=b;
00265     else
00266       activeBegin=b;
00267   } else {
00268     index_t tmpp=entries[a].prev, tmpn=entries[a].next;
00269     entries[a].prev=entries[b].prev;
00270     entries[a].next=entries[b].next;
00271     entries[b].prev=tmpp;
00272     entries[b].next=tmpn;
00273     if(entries[a].prev!=end())
00274       entries[entries[a].prev].next=a;
00275     else
00276       activeBegin=a;
00277     if(entries[a].next!=end())
00278       entries[entries[a].next].prev=a;
00279     else
00280       activeBack=a;
00281     if(entries[b].prev!=end())
00282       entries[entries[b].prev].next=b;
00283     else
00284       activeBegin=b;
00285     if(entries[b].next!=end())
00286       entries[entries[b].next].prev=b;
00287     else
00288       activeBack=b;
00289   }
00290   /*
00291   // Front Back => Front Back
00292   //  a     b       b     a
00293   //  a     -       b     -
00294   //  b     a       a     b
00295   //  b     -       a     -
00296   //  -     a       -     b
00297   //  -     b       -     a
00298   if(activeBegin==a) {
00299     activeBegin=b;
00300     if(activeBack==b)
00301       activeBack=a;
00302   } else if(activeBegin==b) {
00303     activeBegin=a;
00304     if(activeBack==a)
00305       activeBack=b;
00306   } else {
00307     if(activeBack==a)
00308       activeBack=b;
00309     else if(activeBack=b)
00310       activeBack==a;
00311   }
00312   */
00313 }
00314 
00315 /*! free list is a queue... pop front, push back - hopefully more robust with multi-threads
00316     is purposely sloppy with unused links, a little faster*/
00317 template < class T, unsigned int MAX, class index_t >
00318 index_t
00319 ListMemBuf<T,MAX,index_t>::pop_free() {
00320   if(freeBegin==end())
00321     return end();
00322   index_t tmp=freeBegin;
00323   if(freeBegin==freeBack)
00324     freeBegin=freeBack=end();
00325   else
00326     freeBegin=entries[freeBegin].next;
00327   cursize++;
00328   new (entries[tmp].data) T;  //calls constructor so that the data is "fresh"
00329   return tmp;
00330 }
00331 
00332 /*! @see pop_free() */
00333 template < class T, unsigned int MAX, class index_t >
00334 void
00335 ListMemBuf<T,MAX,index_t>::push_free(index_t x) {
00336   if(freeBack==end())
00337     freeBegin=x;
00338   else
00339     entries[freeBack].next=x;
00340   freeBack=x;
00341   cursize--;
00342   operator[](x).~T(); //to match the constructor call in pop_free() (or the entry_t constructor during initialization)
00343 }
00344 
00345 /*! @file
00346  * @brief Defines ListMemBuf, which provides some degree of dynamic allocation of a templated type from a buffer of set size.
00347  * @author ejt (Creator)
00348  */
00349  
00350  #endif

Tekkotsu v5.1CVS
Generated Mon May 9 04:58:43 2016 by Doxygen 1.6.3