CrystalSpace

Public API Reference

Main Page | Modules | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | File Members | Related Pages

blockallocator.h

Go to the documentation of this file.
00001 /*
00002   Crystal Space Generic Object Block Allocator
00003   Copyright (C)2005 by Eric sunshine <sunshine@sunshineco.com>
00004 
00005   This library is free software; you can redistribute it and/or
00006   modify it under the terms of the GNU Library General Public
00007   License as published by the Free Software Foundation; either
00008   version 2 of the License, or (at your option) any later version.
00009 
00010   This library is distributed in the hope that it will be useful,
00011   but WITHOUT ANY WARRANTY; without even the implied warranty of
00012   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013   Library General Public License for more details.
00014 
00015   You should have received a copy of the GNU Library General Public
00016   License along with this library; if not, write to the Free
00017   Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00018 */
00019 #ifndef __CSUTIL_BLOCK_ALLOCATOR_H__
00020 #define __CSUTIL_BLOCK_ALLOCATOR_H__
00021 
00026 #include "csextern.h"
00027 #include "csutil/array.h"
00028 #include "csutil/bitarray.h"
00029 #include "csutil/sysfunc.h"
00030 
00031 // hack: work around problems caused by #defining 'new'
00032 #if defined(CS_EXTENSIVE_MEMDEBUG) || defined(CS_MEMORY_TRACKER)
00033 # undef new
00034 #endif
00035 #include <new>
00036 
00037 #ifdef CS_MEMORY_TRACKER
00038 #include "csutil/memdebug.h"
00039 #include <typeinfo>
00040 #endif
00041 
00046 class csBlockAllocatorNormalBlockPolicy
00047 {
00048 public:
00052   static inline uint8* AllocBlock (size_t blocksize) 
00053   {
00054     return (uint8*)malloc(blocksize);
00055   }
00056 
00061   static inline void FreeBlock(uint8* p)
00062   {
00063     free (p);
00064   }
00065 };
00066 
00072 template <size_t A = 1>
00073 class csBlockAllocatorAlignPolicy
00074 {
00075 public:
00079   static inline uint8* AllocBlock(size_t blocksize) 
00080   {
00081     return (uint8*)csAlignedMalloc (blocksize, A);
00082   }
00083 
00088   static inline void FreeBlock(uint8* p)
00089   {
00090     csAlignedFree (p);
00091   }
00092 };
00093 
00094 #ifdef CS_MEMORY_TRACKER
00095 
00100 class csBlockAllocatorMTBlockPolicy
00101 {
00102 public:
00106   static inline uint8* AllocBlock (size_t blocksize) const
00107   {
00108     char buf[255];
00109     sprintf (buf, "csBlockAllocator<%s>", typeid (T).name());
00110     int32* ptr = (int32*)malloc (blocksize + sizeof (int32)*2);
00111     *ptr++ = (int32)mtiRegisterAlloc (blocksize, buf);
00112     *ptr++ = blocksize;
00113     return (uint8*)ptr;
00114   }
00115 
00120   static inline void FreeBlock(uint8* p) const
00121   {
00122     int32* ptr = ((int32*)p)-2;
00123     mtiRegisterFree ((csMemTrackerInfo*)*ptr, (size_t)ptr[1]);
00124     free (ptr);
00125   }
00126 };
00127 #endif
00128 
00149 #ifdef CS_MEMORY_TRACKER
00150 template <class T, class BlockPolicy = csBlockAllocatorMTBlockPolicy>
00151 #else
00152 template <class T, class BlockPolicy = csBlockAllocatorNormalBlockPolicy>
00153 #endif
00154 class csBlockAllocator
00155 {
00156 protected: // 'protected' allows access by test-suite.
00157   struct FreeNode
00158   {
00159     FreeNode* next;
00160   };
00161 
00162   struct BlockKey
00163   {
00164     uint8 const* addr;
00165     size_t blocksize;
00166     BlockKey(uint8 const* p, size_t n) : addr(p), blocksize(n) {}
00167   };
00168 
00169   csArray<uint8*> blocks;       // List of allocated blocks; sorted by address.
00170   size_t size;                  // Number of elements per block.
00171   size_t elsize;                // Element size; >= sizeof(void*).
00172   size_t blocksize;             // Size in bytes per block.
00173   FreeNode* freenode;           // Head of the chain of free nodes.
00174   bool pedantic;                // Warn about nodes not explicitly freed.
00175   bool insideDisposeAll;        // Flag to ignore calls to Compact() and
00176                                 //  Free() if they're called recursively
00177                                 //  while disposing the entire allocation set.
00178                                 //  Recursive calls to Alloc() will signal an
00179                                 //  assertion failure.
00180 
00187   static int FuzzyCmp(uint8* const& block, BlockKey const& k)
00188   {
00189     return (block + k.blocksize <= k.addr ? -1 : (block > k.addr ? 1 : 0));
00190   }
00191 
00195   size_t FindBlock(void const* m) const
00196   {
00197     return blocks.FindSortedKey(
00198       csArrayCmp<uint8*,BlockKey>(BlockKey((uint8*)m, blocksize), FuzzyCmp));
00199   }
00200 
00206   uint8* AllocBlock() const
00207   {
00208     uint8* block = BlockPolicy::AllocBlock(blocksize);
00209 
00210     // Build the free-node chain (all nodes are free in the new block).
00211     FreeNode* nextfree = 0;
00212     uint8* node = block + (size - 1) * elsize;
00213     for ( ; node >= block; node -= elsize)
00214     {
00215       FreeNode* slot = (FreeNode*)node;
00216       slot->next = nextfree;
00217       nextfree = slot;
00218     }
00219     CS_ASSERT((uint8*)nextfree == block);
00220     return block;
00221   }
00222 
00226   void FreeBlock(uint8* p) const
00227   {
00228     BlockPolicy::FreeBlock(p);
00229   }
00230 
00234   void DestroyObject(T* p, bool warn = false) const
00235   {
00236     p->~T();
00237     if (warn)
00238     {
00239 #ifdef CS_DEBUG
00240       csPrintfErr("NOTIFY: csBlockAllocator(%p) destroying potentially leaked "
00241                   "object at %p.\n", (void*)this, (void*)p);
00242 #endif
00243     }
00244 #ifdef CS_BLOCKALLOC_DEBUG
00245     memset (p, 0xfb, elsize);
00246 #endif
00247   }
00248 
00253   csBitArray GetAllocationMap() const
00254   {
00255     csBitArray mask(size * blocks.GetSize());
00256     mask.FlipAllBits();
00257     for (FreeNode* p = freenode; p != 0; p = p->next)
00258     {
00259       size_t const n = FindBlock(p);
00260       CS_ASSERT(n != csArrayItemNotFound);
00261       size_t const slot = ((uint8*)p - blocks[n]) / elsize; // Slot in block.
00262       mask.ClearBit(n * size + slot);
00263     }
00264     return mask;
00265   }
00266 
00272   void DisposeAll(bool warn_unfreed)
00273   {
00274     insideDisposeAll = true;
00275     csBitArray const mask(GetAllocationMap());
00276     size_t node = 0;
00277     for (size_t b = 0, bN = blocks.GetSize(); b < bN; b++)
00278     {
00279       for (uint8 *p = blocks[b], *pN = p + blocksize; p < pN; p += elsize)
00280           if (mask.IsBitSet(node++))
00281             DestroyObject((T*)p, warn_unfreed);
00282       FreeBlock(blocks[b]);
00283     }
00284     blocks.DeleteAll();
00285     freenode = 0;
00286     insideDisposeAll = false;
00287   }
00288 
00289 public:
00311   csBlockAllocator(size_t nelem = 32, bool warn_unfreed = false) :
00312     size(nelem), elsize(sizeof(T)), freenode(0), pedantic(warn_unfreed),
00313     insideDisposeAll(false)
00314   {
00315     if (elsize < sizeof (FreeNode))
00316       elsize = sizeof (FreeNode);
00317     blocksize = elsize * size;
00318   }
00319 
00323   ~csBlockAllocator()
00324   {
00325     DisposeAll(pedantic);
00326   }
00327 
00333   void Empty()
00334   {
00335     DisposeAll(false);
00336   }
00337 
00342   void Compact()
00343   {
00344     if (insideDisposeAll) return;
00345 
00346     bool compacted = false;
00347     csBitArray mask(GetAllocationMap());
00348     for (size_t b = blocks.GetSize(); b-- > 0; )
00349     {
00350       size_t const node = b * size;
00351       if (!mask.AreSomeBitsSet(node, size))
00352       {
00353         FreeBlock(blocks[b]);
00354         blocks.DeleteIndex(b);
00355         mask.Delete(node, size);
00356         compacted = true;
00357       }
00358     }
00359 
00360     // If blocks were deleted, then free-node chain broke, so rebuild it.
00361     if (compacted)
00362     {
00363       FreeNode* nextfree = 0;
00364       size_t const bN = blocks.GetSize();
00365       size_t node = bN * size;
00366       for (size_t b = bN; b-- > 0; )
00367       {
00368         uint8* const p0 = blocks[b];
00369         for (uint8* p = p0 + (size - 1) * elsize; p >= p0; p -= elsize)
00370         {
00371           if (!mask.IsBitSet(--node))
00372           {
00373             FreeNode* slot = (FreeNode*)p;
00374             slot->next = nextfree;
00375             nextfree = slot;
00376           }
00377         }
00378       }
00379       freenode = nextfree;
00380     }
00381   }
00382 
00386   T* Alloc()
00387   {
00388     if (insideDisposeAll)
00389     {
00390       csPrintfErr("ERROR: csBlockAllocator(%p) tried to allocate memory while inside DisposeAll()", (void*)this);
00391       CS_ASSERT(false);
00392     }
00393 
00394     if (freenode == 0)
00395     {
00396       uint8* p = AllocBlock();
00397       blocks.InsertSorted(p);
00398       freenode = (FreeNode*)p;
00399     }
00400     void* const node = freenode;
00401     freenode = freenode->next;
00402     return new (node) T;
00403   }
00404 
00408   void Free(T* p)
00409   {
00410     if (p != 0 && !insideDisposeAll)
00411     {
00412       CS_ASSERT(FindBlock(p) != csArrayItemNotFound);
00413       DestroyObject(p, false);
00414       FreeNode* f = (FreeNode*)p;
00415       f->next = freenode;
00416       freenode = f;
00417     }
00418   }
00420   size_t GetBlockElements() const { return size; }
00421 };
00422 
00423 #if defined(CS_EXTENSIVE_MEMDEBUG) || defined(CS_MEMORY_TRACKER)
00424 # define new CS_EXTENSIVE_MEMDEBUG_NEW
00425 #endif
00426 
00427 #endif // __CSUTIL_BLOCK_ALLOCATOR_H__

Generated for Crystal Space by doxygen 1.4.4