CrystalSpace

Public API Reference

csutil/fixedsizeallocator.h

Go to the documentation of this file.
00001 /*
00002   Crystal Space Fixed Size Block Allocator
00003   Copyright (C) 2005 by Eric Sunshine <[email protected]>
00004             (C) 2006 by Frank Richter
00005 
00006   This library is free software; you can redistribute it and/or
00007   modify it under the terms of the GNU Library General Public
00008   License as published by the Free Software Foundation; either
00009   version 2 of the License, or (at your option) any later version.
00010 
00011   This library is distributed in the hope that it will be useful,
00012   but WITHOUT ANY WARRANTY; without even the implied warranty of
00013   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014   Library General Public License for more details.
00015 
00016   You should have received a copy of the GNU Library General Public
00017   License along with this library; if not, write to the Free
00018   Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00019 */
00020 #ifndef __CSUTIL_FIXEDSIZEALLOCATOR_H__
00021 #define __CSUTIL_FIXEDSIZEALLOCATOR_H__
00022 
00027 #include "csextern.h"
00028 #include "csutil/array.h"
00029 #include "csutil/bitarray.h"
00030 #include "csutil/sysfunc.h"
00031 
00032 #ifdef CS_DEBUG
00033 #include <typeinfo>
00034 #endif
00035 
00054 template <size_t Size, class Allocator = CS::Memory::AllocatorMalloc>
00055 class csFixedSizeAllocator
00056 {
00057 public:
00058   typedef csFixedSizeAllocator<Size, Allocator> ThisType;
00059   typedef Allocator AllocatorType;
00060 
00061 protected: // 'protected' allows access by test-suite.
00062   struct FreeNode
00063   {
00064     FreeNode* next;
00065   };
00066 
00067   struct BlockKey
00068   {
00069     uint8 const* addr;
00070     size_t blocksize;
00071     BlockKey(uint8 const* p, size_t n) : addr(p), blocksize(n) {}
00072   };
00073 
00074   struct BlocksWrapper : public Allocator
00075   {
00076     csArray<uint8*> b;
00077   };
00079   BlocksWrapper blocks;
00081   size_t elcount;
00083   size_t elsize;
00085   size_t blocksize;
00087   FreeNode* freenode;
00093   bool insideDisposeAll;
00094 
00101   static int FuzzyCmp(uint8* const& block, BlockKey const& k)
00102   {
00103     return (block + k.blocksize <= k.addr ? -1 : (block > k.addr ? 1 : 0));
00104   }
00105 
00109   size_t FindBlock(void const* m) const
00110   {
00111     return blocks.b.FindSortedKey(
00112       csArrayCmp<uint8*,BlockKey>(BlockKey((uint8*)m, blocksize), FuzzyCmp));
00113   }
00114 
00120   uint8* AllocBlock()
00121   {
00122     uint8* block = (uint8*)blocks.Alloc (blocksize);
00123 
00124     // Build the free-node chain (all nodes are free in the new block).
00125     FreeNode* nextfree = 0;
00126     uint8* node = block + (elcount - 1) * elsize;
00127     for ( ; node >= block; node -= elsize)
00128     {
00129       FreeNode* slot = (FreeNode*)node;
00130       slot->next = nextfree;
00131       nextfree = slot;
00132     }
00133     CS_ASSERT((uint8*)nextfree == block);
00134     return block;
00135   }
00136 
00140   void FreeBlock(uint8* p)
00141   {
00142     blocks.Free (p);
00143   }
00144 
00148   template<typename Disposer>
00149   void DestroyObject (Disposer& disposer, void* p) const
00150   {
00151     disposer.Dispose (p);
00152 #ifdef CS_FIXEDSIZEALLOC_DEBUG
00153     memset (p, 0xfb, elsize);
00154 #endif
00155   }
00156 
00161   csBitArray GetAllocationMap() const
00162   {
00163     csBitArray mask(elcount * blocks.b.GetSize());
00164     mask.FlipAllBits();
00165     for (FreeNode* p = freenode; p != 0; p = p->next)
00166     {
00167       size_t const n = FindBlock(p);
00168       CS_ASSERT(n != csArrayItemNotFound);
00169       size_t const slot = ((uint8*)p - blocks.b[n]) / elsize; // Slot in block.
00170       mask.ClearBit(n * elcount + slot);
00171     }
00172     return mask;
00173   }
00174 
00176   class DefaultDisposer
00177   {
00178   #ifdef CS_DEBUG
00179     bool doWarn;
00180     const char* parentClass;
00181     const void* parent;
00182     size_t count;
00183   #endif
00184   public:
00185   #ifdef CS_DEBUG
00186     template<typename BA>
00187     DefaultDisposer (const BA& ba, bool legit) :
00188       doWarn (!legit), parentClass (typeid(BA).name()), parent (&ba),
00189       count (0)
00190     { 
00191     }
00192   #else
00193     template<typename BA>
00194     DefaultDisposer (const BA&, bool legit)
00195     { (void)legit; }
00196   #endif
00197     ~DefaultDisposer()
00198     {
00199   #ifdef CS_DEBUG
00200       if ((count > 0) && doWarn)
00201       {
00202         csPrintfErr("%s %p leaked %zu objects.\n", parentClass, (void*)this, 
00203           count);
00204       }
00205   #endif
00206     }
00207     void Dispose (void* /*p*/) 
00208     {
00209   #ifdef CS_DEBUG
00210       count++;
00211   #endif
00212     }
00213   };
00219   template<typename Disposer>
00220   void DisposeAll(Disposer& disposer)
00221   {
00222     insideDisposeAll = true;
00223     csBitArray const mask(GetAllocationMap());
00224     size_t node = 0;
00225     for (size_t b = 0, bN = blocks.b.GetSize(); b < bN; b++)
00226     {
00227       for (uint8 *p = blocks.b[b], *pN = p + blocksize; p < pN; p += elsize)
00228         if (mask.IsBitSet(node++))
00229           DestroyObject (disposer, p);
00230       FreeBlock(blocks.b[b]);
00231     }
00232     blocks.b.DeleteAll();
00233     freenode = 0;
00234     insideDisposeAll = false;
00235   }
00236 
00242   template<typename Disposer>
00243   void Free (Disposer& disposer, void* p)
00244   {
00245     if (p != 0 && !insideDisposeAll)
00246     {
00247       CS_ASSERT(FindBlock(p) != csArrayItemNotFound);
00248       DestroyObject (disposer, p);
00249       FreeNode* f = (FreeNode*)p;
00250       f->next = freenode;
00251       freenode = f;
00252     }
00253   }
00260   template<typename Disposer>
00261   bool TryFree (Disposer& disposer, void* p)
00262   {
00263     if (p != 0 && !insideDisposeAll)
00264     {
00265       if (FindBlock(p) == csArrayItemNotFound) return false;
00266       DestroyObject (disposer, p);
00267       FreeNode* f = (FreeNode*)p;
00268       f->next = freenode;
00269       freenode = f;
00270     }
00271     return true;
00272   }
00273 
00275   void* AllocCommon ()
00276   {
00277     if (insideDisposeAll)
00278     {
00279       csPrintfErr("ERROR: csFixedSizeAllocator(%p) tried to allocate memory "
00280         "while inside DisposeAll()", (void*)this);
00281       CS_ASSERT(false);
00282     }
00283 
00284     if (freenode == 0)
00285     {
00286       uint8* p = AllocBlock();
00287       blocks.b.InsertSorted(p);
00288       freenode = (FreeNode*)p;
00289     }
00290     void* const node = freenode;
00291     freenode = freenode->next;
00292     return node;
00293   }
00294 private:
00295   csFixedSizeAllocator (csFixedSizeAllocator const&);  // Illegal; unimplemented.
00296   void operator= (csFixedSizeAllocator const&);         // Illegal; unimplemented.
00297 public:
00308   csFixedSizeAllocator (size_t nelem = 32) :
00309     elcount (nelem), elsize(Size), freenode(0), insideDisposeAll(false)
00310   {
00311 #ifdef CS_MEMORY_TRACKER
00312     blocks.SetMemTrackerInfo (typeid(*this).name());
00313 #endif
00314     if (elsize < sizeof (FreeNode))
00315       elsize = sizeof (FreeNode);
00316     blocksize = elsize * elcount;
00317   }
00318 
00322   ~csFixedSizeAllocator()
00323   {
00324     DefaultDisposer disposer (*this, false);
00325     DisposeAll (disposer);
00326   }
00327 
00333   void Empty()
00334   {
00335     DefaultDisposer disposer (*this, true);
00336     DisposeAll (disposer);
00337   }
00338 
00343   void Compact()
00344   {
00345     if (insideDisposeAll) return;
00346 
00347     bool compacted = false;
00348     csBitArray mask(GetAllocationMap());
00349     for (size_t b = blocks.b.GetSize(); b-- > 0; )
00350     {
00351       size_t const node = b * elcount;
00352       if (!mask.AreSomeBitsSet(node, elcount))
00353       {
00354         FreeBlock(blocks.b[b]);
00355         blocks.b.DeleteIndex(b);
00356         mask.Delete(node, elcount);
00357         compacted = true;
00358       }
00359     }
00360 
00361     // If blocks were deleted, then free-node chain broke, so rebuild it.
00362     if (compacted)
00363     {
00364       FreeNode* nextfree = 0;
00365       size_t const bN = blocks.b.GetSize();
00366       size_t node = bN * elcount;
00367       for (size_t b = bN; b-- > 0; )
00368       {
00369         uint8* const p0 = blocks.b[b];
00370         for (uint8* p = p0 + (elcount - 1) * elsize; p >= p0; p -= elsize)
00371         {
00372           if (!mask.IsBitSet(--node))
00373           {
00374             FreeNode* slot = (FreeNode*)p;
00375             slot->next = nextfree;
00376             nextfree = slot;
00377           }
00378         }
00379       }
00380       freenode = nextfree;
00381     }
00382   }
00383 
00387   void* Alloc ()
00388   {
00389     return AllocCommon();
00390   }
00391 
00396   void Free (void* p)
00397   {
00398     DefaultDisposer disposer (*this, true);
00399     Free (disposer, p);
00400   }
00407   bool TryFree (void* p)
00408   {
00409     DefaultDisposer disposer (*this, true);
00410     return TryFree (disposer, p);
00411   }
00413   size_t GetBlockElements() const { return elcount; }
00414   
00417   void* Alloc (size_t n)
00418   {
00419     CS_ASSERT (n == Size);
00420     return Alloc();
00421   }
00422   void* Alloc (void* p, size_t newSize)
00423   {
00424     CS_ASSERT (newSize == Size);
00425     return p;
00426   }
00427   void SetMemTrackerInfo (const char* /*info*/) { }
00429 };
00430 
00433 #endif // __CSUTIL_FIXEDSIZEALLOCATOR_H__

Generated for Crystal Space by doxygen 1.4.7