csutil/fixedsizeallocator.h Source File (Crystal Space 2.0 Public API Reference)

| ## Public API Reference

- Main Page
- Related Pages
- Modules
- Namespaces
- Classes
- Files

- File List
- File Members

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 <sunshine@sunshineco.com>
    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
    00036 #if defined(CS_DEBUG) && !defined(CS_FIXEDSIZEALLOC_DEBUG)
    00037 #define _CS_FIXEDSIZEALLOC_DEBUG_DEFAULTED
    00038 #define CS_FIXEDSIZEALLOC_DEBUG
    00039 #endif
    00040
    00060 template <size_t Size, class Allocator = CS::Memory::AllocatorMalloc>
    00061 class csFixedSizeAllocator
    00062 {
    00063 public:
    00064   typedef csFixedSizeAllocator<Size, Allocator> ThisType;
    00065   typedef Allocator AllocatorType;
    00066
    00067 protected: // 'protected' allows access by test-suite.
    00068   struct FreeNode
    00069   {
    00070     FreeNode* next;
    00071   };
    00072
    00073   struct BlockKey
    00074   {
    00075     uint8 const* addr;
    00076     size_t blocksize;
    00077     BlockKey(uint8 const* p, size_t n) : addr(p), blocksize(n) {}
    00078   };
    00079
    00080   struct BlocksWrapper : public Allocator
    00081   {
    00082     csArray<uint8*> b;
    00083
    00084     BlocksWrapper () {}
    00085     BlocksWrapper (const Allocator& alloc) : Allocator (alloc) {}
    00086   };
    00088   BlocksWrapper blocks;
    00090   size_t elcount;
    00092   size_t elsize;
    00094   size_t blocksize;
    00096   FreeNode* freenode;
    00102   bool insideDisposeAll;
    00103
    00110   static int FuzzyCmp(uint8* const& block, BlockKey const& k)
    00111   {
    00112     return (block + k.blocksize <= k.addr ? -1 : (block > k.addr ? 1 : 0));
    00113   }
    00114
    00118   size_t FindBlock(void const* m) const
    00119   {
    00120     return blocks.b.FindSortedKey(
    00121       csArrayCmp<uint8*,BlockKey>(BlockKey((uint8*)m, blocksize), FuzzyCmp));
    00122   }
    00123
    00129   uint8* AllocBlock()
    00130   {
    00131     uint8* block = (uint8*)blocks.Alloc (blocksize);
    00132
    00133     // Build the free-node chain (all nodes are free in the new block).
    00134     FreeNode* nextfree = 0;
    00135     uint8* node = block + (elcount - 1) * elsize;
    00136     for ( ; node >= block; node -= elsize)
    00137     {
    00138       FreeNode* slot = (FreeNode*)node;
    00139       slot->next = nextfree;
    00140       nextfree = slot;
    00141     }
    00142     CS_ASSERT((uint8*)nextfree == block);
    00143     return block;
    00144   }
    00145
    00149   void FreeBlock(uint8* p)
    00150   {
    00151     blocks.Free (p);
    00152   }
    00153
    00157   template<typename Disposer>
    00158   void DestroyObject (Disposer& disposer, void* p) const
    00159   {
    00160     disposer.Dispose (p);
    00161 #ifdef CS_FIXEDSIZEALLOC_DEBUG
    00162     memset (p, 0xfb, elsize);
    00163 #endif
    00164   }
    00165
    00170   csBitArray GetAllocationMap() const
    00171   {
    00172     csBitArray mask(elcount * blocks.b.GetSize());
    00173     mask.FlipAllBits();
    00174     for (FreeNode* p = freenode; p != 0; p = p->next)
    00175     {
    00176       size_t const n = FindBlock(p);
    00177       CS_ASSERT(n != csArrayItemNotFound);
    00178       size_t const slot = ((uint8*)p - blocks.b[n]) / elsize; // Slot in block.
    00179       mask.ClearBit(n * elcount + slot);
    00180     }
    00181     return mask;
    00182   }
    00183
    00185   class DefaultDisposer
    00186   {
    00187   #ifdef CS_DEBUG
    00188     bool doWarn;
    00189     const char* parentClass;
    00190     const void* parent;
    00191     size_t count;
    00192   #endif
    00193   public:
    00194   #ifdef CS_DEBUG
    00195     template<typename BA>
    00196     DefaultDisposer (const BA& ba, bool legit) :
    00197       doWarn (!legit), parentClass (typeid(BA).name()), parent (&ba),
    00198       count (0)
    00199     {
    00200     }
    00201   #else
    00202     template<typename BA>
    00203     DefaultDisposer (const BA&, bool legit)
    00204     { (void)legit; }
    00205   #endif
    00206     ~DefaultDisposer()
    00207     {
    00208   #ifdef CS_DEBUG
    00209       if ((count > 0) && doWarn)
    00210       {
    00211         csPrintfErr("%s %p leaked %zu objects.\n", parentClass, (void*)this,
    00212           count);
    00213       }
    00214   #endif
    00215     }
    00216     void Dispose (void* /*p*/)
    00217     {
    00218   #ifdef CS_DEBUG
    00219       count++;
    00220   #endif
    00221     }
    00222   };
    00228   template<typename Disposer>
    00229   void DisposeAll(Disposer& disposer)
    00230   {
    00231     insideDisposeAll = true;
    00232     csBitArray const mask(GetAllocationMap());
    00233     size_t node = 0;
    00234     for (size_t b = 0, bN = blocks.b.GetSize(); b < bN; b++)
    00235     {
    00236       for (uint8 *p = blocks.b[b], *pN = p + blocksize; p < pN; p += elsize)
    00237         if (mask.IsBitSet(node++))
    00238           DestroyObject (disposer, p);
    00239       FreeBlock(blocks.b[b]);
    00240     }
    00241     blocks.b.DeleteAll();
    00242     freenode = 0;
    00243     insideDisposeAll = false;
    00244   }
    00245
    00251   template<typename Disposer>
    00252   void Free (Disposer& disposer, void* p)
    00253   {
    00254     if (p != 0 && !insideDisposeAll)
    00255     {
    00256       CS_ASSERT(FindBlock(p) != csArrayItemNotFound);
    00257       DestroyObject (disposer, p);
    00258       FreeNode* f = (FreeNode*)p;
    00259       f->next = freenode;
    00260       freenode = f;
    00261     }
    00262   }
    00269   template<typename Disposer>
    00270   bool TryFree (Disposer& disposer, void* p)
    00271   {
    00272     if (p != 0 && !insideDisposeAll)
    00273     {
    00274       if (FindBlock(p) == csArrayItemNotFound) return false;
    00275       DestroyObject (disposer, p);
    00276       FreeNode* f = (FreeNode*)p;
    00277       f->next = freenode;
    00278       freenode = f;
    00279     }
    00280     return true;
    00281   }
    00287   template<typename Disposer>
    00288   void FreeAll (Disposer& disposer)
    00289   {
    00290     insideDisposeAll = true;
    00291     csBitArray const mask(GetAllocationMap());
    00292     size_t node = 0;
    00293     for (size_t b = 0, bN = blocks.b.GetSize(); b < bN; b++)
    00294     {
    00295       for (uint8 *p = blocks.b[b], *pN = p + blocksize; p < pN; p += elsize)
    00296       {
    00297         if (mask.IsBitSet(node++))
    00298         {
    00299           DestroyObject (disposer, p);
    00300           FreeNode* f = (FreeNode*)p;
    00301           f->next = freenode;
    00302           freenode = f;
    00303         }
    00304       }
    00305     }
    00306     insideDisposeAll = false;
    00307   }
    00308
    00310   void* AllocCommon ()
    00311   {
    00312     if (insideDisposeAll)
    00313     {
    00314       csPrintfErr("ERROR: csFixedSizeAllocator(%p) tried to allocate memory "
    00315         "while inside DisposeAll()", (void*)this);
    00316       CS_ASSERT(false);
    00317     }
    00318
    00319     if (freenode == 0)
    00320     {
    00321       uint8* p = AllocBlock();
    00322       blocks.b.InsertSorted(p);
    00323       freenode = (FreeNode*)p;
    00324     }
    00325     union
    00326     {
    00327       FreeNode* a;
    00328       void* b;
    00329     } pun;
    00330     pun.a = freenode;
    00331     freenode = freenode->next;
    00332 #ifdef CS_FIXEDSIZEALLOC_DEBUG
    00333     memset (pun.b, 0xfa, elsize);
    00334 #endif
    00335     return pun.b;
    00336   }
    00337 private:
    00338   void operator= (csFixedSizeAllocator const&);         // Illegal; unimplemented.
    00339 public:
    00341
    00351   csFixedSizeAllocator (size_t nelem = 32) :
    00352     elcount (nelem), elsize(Size), freenode(0), insideDisposeAll(false)
    00353   {
    00354 #ifdef CS_MEMORY_TRACKER
    00355     blocks.SetMemTrackerInfo (typeid(*this).name());
    00356 #endif
    00357     if (elsize < sizeof (FreeNode))
    00358       elsize = sizeof (FreeNode);
    00359     blocksize = elsize * elcount;
    00360   }
    00361   csFixedSizeAllocator (size_t nelem, const Allocator& alloc) : blocks (alloc),
    00362     elcount (nelem), elsize(Size), freenode(0), insideDisposeAll(false)
    00363   {
    00364 #ifdef CS_MEMORY_TRACKER
    00365     blocks.SetMemTrackerInfo (typeid(*this).name());
    00366 #endif
    00367     if (elsize < sizeof (FreeNode))
    00368       elsize = sizeof (FreeNode);
    00369     blocksize = elsize * elcount;
    00370   }
    00372
    00380   csFixedSizeAllocator (csFixedSizeAllocator const& other) :
    00381     elcount (other.elcount), elsize (other.elsize),
    00382     blocksize (other.blocksize), freenode (0), insideDisposeAll (false)
    00383   {
    00384 #ifdef CS_MEMORY_TRACKER
    00385     blocks.SetMemTrackerInfo (typeid(*this).name());
    00386 #endif
    00387     /* Technically, an allocator can be empty even with freenode != 0 */
    00388     CS_ASSERT(other.freenode == 0);
    00389   }
    00390
    00394   ~csFixedSizeAllocator()
    00395   {
    00396     DefaultDisposer disposer (*this, false);
    00397     DisposeAll (disposer);
    00398   }
    00399
    00405   void Empty()
    00406   {
    00407     DefaultDisposer disposer (*this, true);
    00408     DisposeAll (disposer);
    00409   }
    00410
    00415   void Compact()
    00416   {
    00417     if (insideDisposeAll) return;
    00418
    00419     bool compacted = false;
    00420     csBitArray mask(GetAllocationMap());
    00421     for (size_t b = blocks.b.GetSize(); b-- > 0; )
    00422     {
    00423       size_t const node = b * elcount;
    00424       if (!mask.AreSomeBitsSet(node, elcount))
    00425       {
    00426         FreeBlock(blocks.b[b]);
    00427         blocks.b.DeleteIndex(b);
    00428         mask.Delete(node, elcount);
    00429         compacted = true;
    00430       }
    00431     }
    00432
    00433     // If blocks were deleted, then free-node chain broke, so rebuild it.
    00434     if (compacted)
    00435     {
    00436       FreeNode* nextfree = 0;
    00437       size_t const bN = blocks.b.GetSize();
    00438       size_t node = bN * elcount;
    00439       for (size_t b = bN; b-- > 0; )
    00440       {
    00441         uint8* const p0 = blocks.b[b];
    00442         for (uint8* p = p0 + (elcount - 1) * elsize; p >= p0; p -= elsize)
    00443         {
    00444           if (!mask.IsBitSet(--node))
    00445           {
    00446             FreeNode* slot = (FreeNode*)p;
    00447             slot->next = nextfree;
    00448             nextfree = slot;
    00449           }
    00450         }
    00451       }
    00452       freenode = nextfree;
    00453     }
    00454   }
    00455
    00459   size_t GetAllocatedElems() const
    00460   {
    00461     csBitArray mask(GetAllocationMap());
    00462     return mask.NumBitsSet();
    00463   }
    00464
    00468   void* Alloc ()
    00469   {
    00470     return AllocCommon();
    00471   }
    00472
    00477   void Free (void* p)
    00478   {
    00479     DefaultDisposer disposer (*this, true);
    00480     Free (disposer, p);
    00481   }
    00488   bool TryFree (void* p)
    00489   {
    00490     DefaultDisposer disposer (*this, true);
    00491     return TryFree (disposer, p);
    00492   }
    00494   size_t GetBlockElements() const { return elcount; }
    00495
    00498   void* Alloc (size_t n)
    00499   {
    00500     CS_ASSERT (n == Size);
    00501     return Alloc();
    00502   }
    00503   void* Alloc (void* p, size_t newSize)
    00504   {
    00505     CS_ASSERT (newSize == Size);
    00506     return p;
    00507   }
    00508   void SetMemTrackerInfo (const char* /*info*/) { }
    00510 };
    00511
    00512 namespace CS
    00513 {
    00514   namespace Memory
    00515   {
    00521     template <size_t Size, class Allocator = CS::Memory::AllocatorMalloc>
    00522     class FixedSizeAllocatorSafe :
    00523       public CS::Memory::AllocatorSafe<csFixedSizeAllocator<Size, Allocator> >
    00524     {
    00525     protected:
    00526       typedef csFixedSizeAllocator<Size, Allocator> WrappedAllocatorType;
    00527       typedef CS::Memory::AllocatorSafe<csFixedSizeAllocator<Size, Allocator> >
    00528         AllocatorSafeType;
    00529     public:
    00530       FixedSizeAllocatorSafe (size_t nelem = 32) : AllocatorSafeType (nelem)
    00531       {
    00532       }
    00533       FixedSizeAllocatorSafe (size_t nelem, const Allocator& alloc) :
    00534         AllocatorSafeType (nelem, alloc)
    00535       {
    00536       }
    00537
    00538       FixedSizeAllocatorSafe (FixedSizeAllocatorSafe const& other) :
    00539         AllocatorSafeType (other)
    00540       {
    00541       }
    00542
    00543       void Empty()
    00544       {
    00545         CS::Threading::RecursiveMutexScopedLock lock (AllocatorSafeType::mutex);
    00546         WrappedAllocatorType::Empty();
    00547       }
    00548
    00549       void Compact()
    00550       {
    00551         CS::Threading::RecursiveMutexScopedLock lock (AllocatorSafeType::mutex);
    00552         WrappedAllocatorType::Compact();
    00553       }
    00554
    00555       size_t GetAllocatedElems() const
    00556       {
    00557         CS::Threading::RecursiveMutexScopedLock lock (AllocatorSafeType::mutex);
    00558         return WrappedAllocatorType::GetAllocatedElems();
    00559       }
    00560
    00561       void* Alloc ()
    00562       {
    00563         CS::Threading::RecursiveMutexScopedLock lock (AllocatorSafeType::mutex);
    00564         return WrappedAllocatorType::Alloc();
    00565       }
    00566       using AllocatorSafeType::Alloc;
    00567
    00568       bool TryFree (void* p)
    00569       {
    00570         CS::Threading::RecursiveMutexScopedLock lock (AllocatorSafeType::mutex);
    00571         return WrappedAllocatorType::TryFree (p);
    00572       }
    00573       size_t GetBlockElements() const
    00574       {
    00575         CS::Threading::RecursiveMutexScopedLock lock (AllocatorSafeType::mutex);
    00576         return WrappedAllocatorType::GetBlockElements();
    00577       }
    00578     };
    00579   } // namespace Memory
    00580 } // namespace CS
    00581
    00584 #ifdef _CS_FIXEDSIZEALLOC_DEBUG_DEFAULTED
    00585 #undef CS_FIXEDSIZEALLOC_DEBUG
    00586 #undef _CS_FIXEDSIZEALLOC_DEBUG_DEFAULTED
    00587 #endif
    00588
    00589 #endif // __CSUTIL_FIXEDSIZEALLOCATOR_H__