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

base/SmallObj.cpp

Go to the documentation of this file.
00001 /****************************************************************************
00002   Copyright (C)2001 by Andrei Alexandrescu
00003 
00004   This file is a derivative of a file from the Loki Library written by
00005   Andrei Alexandrescu.  It was distributed by him under the terms
00006   listed below (titled Loki Original Distribution Terms).
00007   In accordance with the terms, this distribution contains the copyright
00008   notice here and the copyright notice and permission notice
00009   in supporting documentation.  The terms do *not* require
00010   redistribution under those same terms.  This code is distributed
00011   to you under the terms of the GNU General Public License (GPL) 
00012   below.  The GPL does not require you to maintain the terms of
00013   the Loki Original Distribution Terms, but you are encouraged to do so.
00014 
00015   This program/file is free software; you can redistribute it and/or modify
00016   it under the terms of the GNU General Public License as published by
00017   the Free Software Foundation; either version 2 of the License, or
00018   (at your option) any later version.
00019   
00020   This program is distributed in the hope that it will be useful,
00021   but WITHOUT ANY WARRANTY; without even the implied warranty of
00022   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00023   GNU General Public License for more details. (http://www.gnu.org)
00024   
00025   You should have received a copy of the GNU General Public License
00026   along with this program; if not, write to the Free Software
00027   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00028   
00029  ****************************************************************************
00030   Loki Original Distribution Terms:
00031 
00032   The Loki Library
00033   Copyright (c) 2001 by Andrei Alexandrescu
00034   This code accompanies the book:
00035   Alexandrescu, Andrei. "Modern C++ Design: Generic Programming and Design 
00036       Patterns Applied". Copyright (c) 2001. Addison-Wesley.
00037   Permission to use, copy, modify, distribute and sell this software for any 
00038       purpose is hereby granted without fee, provided that the above copyright 
00039       notice appear in all copies and that both that copyright notice and this 
00040       permission notice appear in supporting documentation.
00041   The author or Addison-Welsey Longman make no representations about the 
00042       suitability of this software for any purpose. It is provided "as is" 
00043       without express or implied warranty.
00044  ****************************************************************************
00045 
00046   $Id: SmallObj.cpp 1029 2004-02-11 20:45:54Z jungd $
00047   $Revision: 1.1 $
00048   $Date: 2004-02-11 15:45:54 -0500 (Wed, 11 Feb 2004) $
00049   $Author: jungd $
00050  
00051 ****************************************************************************/
00052 
00053 // Last update: March 20, 2001
00054 
00055 #include <base/SmallObj>
00056 #include <cassert>
00057 #include <algorithm>
00058 #include <iostream>
00059 
00060 #include <base/MemoryTracer>
00061 
00062 using namespace base;
00063 
00064 ////////////////////////////////////////////////////////////////////////////////
00065 // FixedAllocator::Chunk::Init
00066 // Initializes a chunk object
00067 ////////////////////////////////////////////////////////////////////////////////
00068 
00069 void FixedAllocator::Chunk::Init(std::size_t blockSize, unsigned char blocks)
00070 {
00071     assert(blockSize > 0);
00072     assert(blocks > 0);
00073     // Overflow check
00074     assert((blockSize * blocks) / blockSize == blocks);
00075     
00076     pData_ = NewNamedObj("FixedAllocator::pData") unsigned char[blockSize * blocks];
00077 
00078     Reset(blockSize, blocks);
00079 }
00080 
00081 ////////////////////////////////////////////////////////////////////////////////
00082 // FixedAllocator::Chunk::Reset
00083 // Clears an already allocated chunk
00084 ////////////////////////////////////////////////////////////////////////////////
00085 
00086 void FixedAllocator::Chunk::Reset(std::size_t blockSize, unsigned char blocks)
00087 {
00088     assert(blockSize > 0);
00089     assert(blocks > 0);
00090     // Overflow check
00091     assert((blockSize * blocks) / blockSize == blocks);
00092 
00093     firstAvailableBlock_ = 0;
00094     blocksAvailable_ = blocks;
00095 
00096     unsigned char i = 0;
00097     unsigned char* p = pData_;
00098     for (; i != blocks; p += blockSize)
00099     {
00100         *p = ++i;
00101     }
00102 }
00103 
00104 ////////////////////////////////////////////////////////////////////////////////
00105 // FixedAllocator::Chunk::Release
00106 // Releases the data managed by a chunk
00107 ////////////////////////////////////////////////////////////////////////////////
00108 
00109 void FixedAllocator::Chunk::Release()
00110 {
00111     delete[] pData_;
00112 }
00113 
00114 ////////////////////////////////////////////////////////////////////////////////
00115 // FixedAllocator::Chunk::Allocate
00116 // Allocates a block from a chunk
00117 ////////////////////////////////////////////////////////////////////////////////
00118 
00119 void* FixedAllocator::Chunk::Allocate(std::size_t blockSize)
00120 {
00121     if (!blocksAvailable_) return 0;
00122     
00123     assert((firstAvailableBlock_ * blockSize) / blockSize == 
00124         firstAvailableBlock_);
00125 
00126     unsigned char* pResult =
00127         pData_ + (firstAvailableBlock_ * blockSize);
00128     firstAvailableBlock_ = *pResult;
00129     --blocksAvailable_;
00130     
00131     return pResult;
00132 }
00133 
00134 ////////////////////////////////////////////////////////////////////////////////
00135 // FixedAllocator::Chunk::Deallocate
00136 // Dellocates a block from a chunk
00137 ////////////////////////////////////////////////////////////////////////////////
00138 
00139 void FixedAllocator::Chunk::Deallocate(void* p, std::size_t blockSize)
00140 {
00141     assert(p >= pData_);
00142 
00143     unsigned char* toRelease = static_cast<unsigned char*>(p);
00144     // Alignment check
00145     assert((toRelease - pData_) % blockSize == 0);
00146 
00147     *toRelease = firstAvailableBlock_;
00148     firstAvailableBlock_ = static_cast<unsigned char>(
00149         (toRelease - pData_) / blockSize);
00150     // Truncation check
00151     assert(firstAvailableBlock_ == (toRelease - pData_) / blockSize);
00152 
00153     ++blocksAvailable_;
00154 }
00155 
00156 ////////////////////////////////////////////////////////////////////////////////
00157 // FixedAllocator::FixedAllocator
00158 // Creates a FixedAllocator object of a fixed block size
00159 ////////////////////////////////////////////////////////////////////////////////
00160 
00161 FixedAllocator::FixedAllocator(std::size_t blockSize)
00162     : blockSize_(blockSize)
00163     , allocChunk_(0)
00164     , deallocChunk_(0)
00165 {
00166     assert(blockSize_ > 0);
00167     
00168     prev_ = next_ = this;
00169 
00170     std::size_t numBlocks = DEFAULT_CHUNK_SIZE / blockSize;
00171     if (numBlocks > UCHAR_MAX) numBlocks = UCHAR_MAX;
00172     else if (numBlocks == 0) numBlocks = 8 * blockSize;
00173     
00174     numBlocks_ = static_cast<unsigned char>(numBlocks);
00175     assert(numBlocks_ == numBlocks);
00176 }
00177 
00178 ////////////////////////////////////////////////////////////////////////////////
00179 // FixedAllocator::FixedAllocator(const FixedAllocator&)
00180 // Creates a FixedAllocator object of a fixed block size
00181 ////////////////////////////////////////////////////////////////////////////////
00182 
00183 FixedAllocator::FixedAllocator(const FixedAllocator& rhs)
00184     : blockSize_(rhs.blockSize_)
00185     , numBlocks_(rhs.numBlocks_)
00186     , chunks_(rhs.chunks_)
00187 {
00188     prev_ = &rhs;
00189     next_ = rhs.next_;
00190     rhs.next_->prev_ = this;
00191     rhs.next_ = this;
00192     
00193     allocChunk_ = rhs.allocChunk_
00194         ? &chunks_.front() + (rhs.allocChunk_ - &rhs.chunks_.front())
00195         : 0;
00196 
00197     deallocChunk_ = rhs.deallocChunk_
00198         ? &chunks_.front() + (rhs.deallocChunk_ - &rhs.chunks_.front())
00199         : 0;
00200 }
00201 
00202 FixedAllocator& FixedAllocator::operator=(const FixedAllocator& rhs)
00203 {
00204     FixedAllocator copy(rhs);
00205     copy.Swap(*this);
00206     return *this;
00207 }
00208 
00209 ////////////////////////////////////////////////////////////////////////////////
00210 // FixedAllocator::~FixedAllocator
00211 ////////////////////////////////////////////////////////////////////////////////
00212 
00213 FixedAllocator::~FixedAllocator()
00214 {
00215     if (prev_ != this)
00216     {
00217         prev_->next_ = next_;
00218         next_->prev_ = prev_;
00219         return;
00220     }
00221     
00222     assert(prev_ == next_);
00223     int allocatedBlocks = 0;
00224     Chunks::iterator i = chunks_.begin();
00225     for (; i != chunks_.end(); ++i)
00226     {
00227       allocatedBlocks += (numBlocks_ - i->blocksAvailable_);
00228 
00229       //assert(i->blocksAvailable_ == numBlocks_);
00230       i->Release();
00231     }
00232     
00233     if (allocatedBlocks > 0)
00234       fprintf(stderr,"SmallObj/FixedAllocator::~FixedAllocator - Warning: %d objects (of size %d) left allocated on allocator destruction.\n",allocatedBlocks,blockSize_);
00235 }
00236 
00237 ////////////////////////////////////////////////////////////////////////////////
00238 // FixedAllocator::Swap
00239 ////////////////////////////////////////////////////////////////////////////////
00240 
00241 void FixedAllocator::Swap(FixedAllocator& rhs)
00242 {
00243     using namespace std;
00244     
00245     swap(blockSize_, rhs.blockSize_);
00246     swap(numBlocks_, rhs.numBlocks_);
00247     chunks_.swap(rhs.chunks_);
00248     swap(allocChunk_, rhs.allocChunk_);
00249     swap(deallocChunk_, rhs.deallocChunk_);
00250 }
00251 
00252 ////////////////////////////////////////////////////////////////////////////////
00253 // FixedAllocator::Allocate
00254 // Allocates a block of fixed size
00255 ////////////////////////////////////////////////////////////////////////////////
00256 
00257 void* FixedAllocator::Allocate()
00258 {
00259     if (allocChunk_ == 0 || allocChunk_->blocksAvailable_ == 0)
00260     {
00261         Chunks::iterator i = chunks_.begin();
00262         for (;; ++i)
00263         {
00264             if (i == chunks_.end())
00265             {
00266                 // Initialize
00267                 chunks_.reserve(chunks_.size() + 1);
00268                 Chunk newChunk;
00269                 newChunk.Init(blockSize_, numBlocks_);
00270                 chunks_.push_back(newChunk);
00271                 allocChunk_ = &chunks_.back();
00272                 deallocChunk_ = &chunks_.front();
00273                 break;
00274             }
00275             if (i->blocksAvailable_ > 0)
00276             {
00277                 allocChunk_ = &*i;
00278                 break;
00279             }
00280         }
00281     }
00282     assert(allocChunk_ != 0);
00283     assert(allocChunk_->blocksAvailable_ > 0);
00284     
00285     return allocChunk_->Allocate(blockSize_);
00286 }
00287 
00288 ////////////////////////////////////////////////////////////////////////////////
00289 // FixedAllocator::Deallocate
00290 // Deallocates a block previously allocated with Allocate
00291 // (undefined behavior if called with the wrong pointer)
00292 ////////////////////////////////////////////////////////////////////////////////
00293 
00294 void FixedAllocator::Deallocate(void* p)
00295 {
00296     assert(!chunks_.empty());
00297     assert(&chunks_.front() <= deallocChunk_);
00298     assert(&chunks_.back() >= deallocChunk_);
00299     
00300     deallocChunk_  = VicinityFind(p);
00301     assert(deallocChunk_);
00302 
00303     DoDeallocate(p);
00304 }
00305 
00306 ////////////////////////////////////////////////////////////////////////////////
00307 // FixedAllocator::VicinityFind (internal)
00308 // Finds the chunk corresponding to a pointer, using an efficient search
00309 ////////////////////////////////////////////////////////////////////////////////
00310 
00311 FixedAllocator::Chunk* FixedAllocator::VicinityFind(void* p)
00312 {
00313     assert(!chunks_.empty());
00314     assert(deallocChunk_);
00315 
00316     const std::size_t chunkLength = numBlocks_ * blockSize_;
00317 
00318     Chunk* lo = deallocChunk_;
00319     Chunk* hi = deallocChunk_ + 1;
00320     Chunk* loBound = &chunks_.front();
00321     Chunk* hiBound = &chunks_.back() + 1;
00322 
00323     for (;;)
00324     {
00325         if (lo)
00326         {
00327             if (p >= lo->pData_ && p < lo->pData_ + chunkLength)
00328             {
00329                 return lo;
00330             }
00331             if (lo == loBound) lo = 0;
00332             else --lo;
00333         }
00334         
00335         if (hi)
00336         {
00337             if (p >= hi->pData_ && p < hi->pData_ + chunkLength)
00338             {
00339                 return hi;
00340             }
00341             if (++hi == hiBound) hi = 0;
00342         }
00343     }
00344     assert(false);
00345     return 0;
00346 }
00347 
00348 ////////////////////////////////////////////////////////////////////////////////
00349 // FixedAllocator::DoDeallocate (internal)
00350 // Performs deallocation. Assumes deallocChunk_ points to the correct chunk
00351 ////////////////////////////////////////////////////////////////////////////////
00352 
00353 void FixedAllocator::DoDeallocate(void* p)
00354 {
00355     assert(deallocChunk_->pData_ <= p);
00356     assert(deallocChunk_->pData_ + numBlocks_ * blockSize_ > p);
00357 
00358     // call into the chunk, will adjust the inner list but won't release memory
00359     deallocChunk_->Deallocate(p, blockSize_);
00360 
00361     if (deallocChunk_->blocksAvailable_ == numBlocks_)
00362     {
00363         // deallocChunk_ is completely free, should we release it? 
00364         
00365         Chunk& lastChunk = chunks_.back();
00366         
00367         if (&lastChunk == deallocChunk_)
00368         {
00369             // check if we have two last chunks empty
00370             
00371             if (chunks_.size() > 1 && 
00372                 deallocChunk_[-1].blocksAvailable_ == numBlocks_)
00373             {
00374                 // Two free chunks, discard the last one
00375                 lastChunk.Release();
00376                 chunks_.pop_back();
00377                 allocChunk_ = deallocChunk_ = &chunks_.front();
00378             }
00379             return;
00380         }
00381         
00382         if (lastChunk.blocksAvailable_ == numBlocks_)
00383         {
00384             // Two free blocks, discard one
00385             lastChunk.Release();
00386             chunks_.pop_back();
00387             allocChunk_ = deallocChunk_;
00388         }
00389         else
00390         {
00391             // move the empty chunk to the end
00392             std::swap(*deallocChunk_, lastChunk);
00393             allocChunk_ = &chunks_.back();
00394         }
00395     }
00396 }
00397 
00398 ////////////////////////////////////////////////////////////////////////////////
00399 // SmallObjAllocator::SmallObjAllocator
00400 // Creates an allocator for small objects given chunk size and maximum 'small'
00401 //     object size
00402 ////////////////////////////////////////////////////////////////////////////////
00403 
00404 SmallObjAllocator::SmallObjAllocator(
00405         std::size_t chunkSize, 
00406         std::size_t maxObjectSize)
00407     : pLastAlloc_(0), pLastDealloc_(0)
00408     , chunkSize_(chunkSize), maxObjectSize_(maxObjectSize) 
00409 {   
00410 }
00411 
00412 ////////////////////////////////////////////////////////////////////////////////
00413 // SmallObjAllocator::Allocate
00414 // Allocates 'numBytes' memory
00415 // Uses an internal pool of FixedAllocator objects for small objects  
00416 ////////////////////////////////////////////////////////////////////////////////
00417 
00418 void* SmallObjAllocator::Allocate(std::size_t numBytes)
00419 {
00420     if (numBytes > maxObjectSize_) return operator new(numBytes,"SmallObjAllocator::Allocate");
00421 
00422     if (pLastAlloc_ && pLastAlloc_->BlockSize() == numBytes)
00423     {
00424         return pLastAlloc_->Allocate();
00425     }
00426     
00427     Pool::iterator i = std::lower_bound(pool_.begin(), pool_.end(), numBytes);
00428     if (i == pool_.end() || i->BlockSize() != numBytes)
00429     {
00430         i = pool_.insert(i, FixedAllocator(numBytes));
00431         pLastDealloc_ = &*pool_.begin();
00432     }
00433     pLastAlloc_ = &*i;
00434     return pLastAlloc_->Allocate();
00435 }
00436 
00437 ////////////////////////////////////////////////////////////////////////////////
00438 // SmallObjAllocator::Deallocate
00439 // Deallocates memory previously allocated with Allocate
00440 // (undefined behavior if you pass any other pointer)
00441 ////////////////////////////////////////////////////////////////////////////////
00442 
00443 void SmallObjAllocator::Deallocate(void* p, std::size_t numBytes)
00444 {
00445     if (numBytes > maxObjectSize_) return operator delete(p);
00446 
00447     if (pLastDealloc_ && pLastDealloc_->BlockSize() == numBytes)
00448     {
00449         pLastDealloc_->Deallocate(p);
00450         return;
00451     }
00452 
00453     Pool::iterator i = std::lower_bound(pool_.begin(), pool_.end(), numBytes);
00454     assert(i != pool_.end());
00455     assert(i->BlockSize() == numBytes);
00456     pLastDealloc_ = &*i;
00457     pLastDealloc_->Deallocate(p);
00458 }
00459 
00460 ////////////////////////////////////////////////////////////////////////////////
00461 // Change log:
00462 // March 20: fix exception safety issue in FixedAllocator::Allocate 
00463 //     (thanks to Chris Udazvinis for pointing that out)
00464 // June 20, 2001: ported by Nick Thurn to gcc 2.95.3. Kudos, Nick!!!
00465 // Feb 25, 2002: David Jung: integrated into larger project - changed to 
00466 //                namespace base and changed header comments to reflect GPL
00467 //                distribution
00468 ////////////////////////////////////////////////////////////////////////////////

Generated on Thu Jul 29 15:56:15 2004 for OpenSim by doxygen 1.3.6