SmallObj.cpp

  1 ////////////////////////////////////////////////////////////////////////////////
  2 // The Loki Library
  3 // Copyright (c) 2001 by Andrei Alexandrescu
  4 // This code accompanies the book:
  5 // Alexandrescu, Andrei. "Modern C++ Design: Generic Programming and Design 
  6 //     Patterns Applied". Copyright (c) 2001. Addison-Wesley.
  7 // Permission to use, copy, modify, distribute and sell this software for any 
  8 //     purpose is hereby granted without fee, provided that the above copyright 
  9 //     notice appear in all copies and that both that copyright notice and this 
 10 //     permission notice appear in supporting documentation.
 11 // The author or Addison-Welsey Longman make no representations about the 
 12 //     suitability of this software for any purpose. It is provided "as is" 
 13 //     without express or implied warranty.
 14 ////////////////////////////////////////////////////////////////////////////////
 15 
 16 // Last update: March 20, 2001
 17 
 18 #include "SmallObj.h"
 19 #include <cassert>
 20 #include <algorithm>
 21 
 22 using namespace Loki;
 23 
 24 ////////////////////////////////////////////////////////////////////////////////
 25 // FixedAllocator::Chunk::Init
 26 // Initializes a chunk object
 27 ////////////////////////////////////////////////////////////////////////////////
 28 
 29 void FixedAllocator::Chunk::Init(std::size_t blockSize, unsigned char blocks)
 30 {
 31     assert(blockSize > 0);
 32     assert(blocks > 0);
 33     // Overflow check
 34     assert((blockSize * blocks) / blockSize == blocks);
 35     
 36     pData_ = new unsigned char[blockSize * blocks];
 37     Reset(blockSize, blocks);
 38 }
 39 
 40 ////////////////////////////////////////////////////////////////////////////////
 41 // FixedAllocator::Chunk::Reset
 42 // Clears an already allocated chunk
 43 ////////////////////////////////////////////////////////////////////////////////
 44 
 45 void FixedAllocator::Chunk::Reset(std::size_t blockSize, unsigned char blocks)
 46 {
 47     assert(blockSize > 0);
 48     assert(blocks > 0);
 49     // Overflow check
 50     assert((blockSize * blocks) / blockSize == blocks);
 51 
 52     firstAvailableBlock_ = 0;
 53     blocksAvailable_ = blocks;
 54 
 55     unsigned char i = 0;
 56     unsigned char* p = pData_;
 57     for (; i != blocks; p += blockSize)
 58     {
 59         *p = ++i;
 60     }
 61 }
 62 
 63 ////////////////////////////////////////////////////////////////////////////////
 64 // FixedAllocator::Chunk::Release
 65 // Releases the data managed by a chunk
 66 ////////////////////////////////////////////////////////////////////////////////
 67 
 68 void FixedAllocator::Chunk::Release()
 69 {
 70     delete[] pData_;
 71 }
 72 
 73 ////////////////////////////////////////////////////////////////////////////////
 74 // FixedAllocator::Chunk::Allocate
 75 // Allocates a block from a chunk
 76 ////////////////////////////////////////////////////////////////////////////////
 77 
 78 void* FixedAllocator::Chunk::Allocate(std::size_t blockSize)
 79 {
 80     if (!blocksAvailable_) return 0;
 81     
 82     assert((firstAvailableBlock_ * blockSize) / blockSize == 
 83         firstAvailableBlock_);
 84 
 85     unsigned char* pResult =
 86         pData_ + (firstAvailableBlock_ * blockSize);
 87     firstAvailableBlock_ = *pResult;
 88     --blocksAvailable_;
 89     
 90     return pResult;
 91 }
 92 
 93 ////////////////////////////////////////////////////////////////////////////////
 94 // FixedAllocator::Chunk::Deallocate
 95 // Dellocates a block from a chunk
 96 ////////////////////////////////////////////////////////////////////////////////
 97 
 98 void FixedAllocator::Chunk::Deallocate(void* p, std::size_t blockSize)
 99 {
100     assert(p >= pData_);
101 
102     unsigned char* toRelease = static_cast<unsigned char*>(p);
103     // Alignment check
104     assert((toRelease - pData_) % blockSize == 0);
105 
106     *toRelease = firstAvailableBlock_;
107     firstAvailableBlock_ = static_cast<unsigned char>(
108         (toRelease - pData_) / blockSize);
109     // Truncation check
110     assert(firstAvailableBlock_ == (toRelease - pData_) / blockSize);
111 
112     ++blocksAvailable_;
113 }
114 
115 ////////////////////////////////////////////////////////////////////////////////
116 // FixedAllocator::FixedAllocator
117 // Creates a FixedAllocator object of a fixed block size
118 ////////////////////////////////////////////////////////////////////////////////
119 
120 FixedAllocator::FixedAllocator(std::size_t blockSize)
121     : blockSize_(blockSize)
122     , allocChunk_(0)
123     , deallocChunk_(0)
124 {
125     assert(blockSize_ > 0);
126     
127     prev_ = next_ = this;
128 
129     std::size_t numBlocks = DEFAULT_CHUNK_SIZE / blockSize;
130     if (numBlocks > UCHAR_MAX) numBlocks = UCHAR_MAX;
131     else if (numBlocks == 0) numBlocks = 8 * blockSize;
132     
133     numBlocks_ = static_cast<unsigned char>(numBlocks);
134     assert(numBlocks_ == numBlocks);
135 }
136 
137 ////////////////////////////////////////////////////////////////////////////////
138 // FixedAllocator::FixedAllocator(const FixedAllocator&)
139 // Creates a FixedAllocator object of a fixed block size
140 ////////////////////////////////////////////////////////////////////////////////
141 
142 FixedAllocator::FixedAllocator(const FixedAllocator& rhs)
143     : blockSize_(rhs.blockSize_)
144     , numBlocks_(rhs.numBlocks_)
145     , chunks_(rhs.chunks_)
146 {
147     prev_ = &rhs;
148     next_ = rhs.next_;
149     rhs.next_->prev_ = this;
150     rhs.next_ = this;
151     
152     allocChunk_ = rhs.allocChunk_
153         ? &chunks_.front() + (rhs.allocChunk_ - &rhs.chunks_.front())
154         : 0;
155 
156     deallocChunk_ = rhs.deallocChunk_
157         ? &chunks_.front() + (rhs.deallocChunk_ - &rhs.chunks_.front())
158         : 0;
159 }
160 
161 FixedAllocator& FixedAllocator::operator=(const FixedAllocator& rhs)
162 {
163     FixedAllocator copy(rhs);
164     copy.Swap(*this);
165     return *this;
166 }
167 
168 ////////////////////////////////////////////////////////////////////////////////
169 // FixedAllocator::~FixedAllocator
170 ////////////////////////////////////////////////////////////////////////////////
171 
172 FixedAllocator::~FixedAllocator()
173 {
174     if (prev_ != this)
175     {
176         prev_->next_ = next_;
177         next_->prev_ = prev_;
178         return;
179     }
180     
181     assert(prev_ == next_);
182     Chunks::iterator i = chunks_.begin();
183     for (; i != chunks_.end(); ++i)
184     {
185        assert(i->blocksAvailable_ == numBlocks_);
186        i->Release();
187     }
188 }
189 
190 ////////////////////////////////////////////////////////////////////////////////
191 // FixedAllocator::Swap
192 ////////////////////////////////////////////////////////////////////////////////
193 
194 void FixedAllocator::Swap(FixedAllocator& rhs)
195 {
196     using namespace std;
197     
198     swap(blockSize_, rhs.blockSize_);
199     swap(numBlocks_, rhs.numBlocks_);
200     chunks_.swap(rhs.chunks_);
201     swap(allocChunk_, rhs.allocChunk_);
202     swap(deallocChunk_, rhs.deallocChunk_);
203 }
204 
205 ////////////////////////////////////////////////////////////////////////////////
206 // FixedAllocator::Allocate
207 // Allocates a block of fixed size
208 ////////////////////////////////////////////////////////////////////////////////
209 
210 void* FixedAllocator::Allocate()
211 {
212     if (allocChunk_ == 0 || allocChunk_->blocksAvailable_ == 0)
213     {
214         Chunks::iterator i = chunks_.begin();
215         for (;; ++i)
216         {
217             if (i == chunks_.end())
218             {
219                 // Initialize
220                 chunks_.reserve(chunks_.size() + 1);
221                 Chunk newChunk;
222                 newChunk.Init(blockSize_, numBlocks_);
223                 chunks_.push_back(newChunk);
224                 allocChunk_ = &chunks_.back();
225                 deallocChunk_ = &chunks_.front();
226                 break;
227             }
228             if (i->blocksAvailable_ > 0)
229             {
230                 allocChunk_ = &*i;
231                 break;
232             }
233         }
234     }
235     assert(allocChunk_ != 0);
236     assert(allocChunk_->blocksAvailable_ > 0);
237     
238     return allocChunk_->Allocate(blockSize_);
239 }
240 
241 ////////////////////////////////////////////////////////////////////////////////
242 // FixedAllocator::Deallocate
243 // Deallocates a block previously allocated with Allocate
244 // (undefined behavior if called with the wrong pointer)
245 ////////////////////////////////////////////////////////////////////////////////
246 
247 void FixedAllocator::Deallocate(void* p)
248 {
249     assert(!chunks_.empty());
250     assert(&chunks_.front() <= deallocChunk_);
251     assert(&chunks_.back() >= deallocChunk_);
252     
253     deallocChunk_  = VicinityFind(p);
254     assert(deallocChunk_);
255 
256     DoDeallocate(p);
257 }
258 
259 ////////////////////////////////////////////////////////////////////////////////
260 // FixedAllocator::VicinityFind (internal)
261 // Finds the chunk corresponding to a pointer, using an efficient search
262 ////////////////////////////////////////////////////////////////////////////////
263 
264 FixedAllocator::Chunk* FixedAllocator::VicinityFind(void* p)
265 {
266     assert(!chunks_.empty());
267     assert(deallocChunk_);
268 
269     const std::size_t chunkLength = numBlocks_ * blockSize_;
270 
271     Chunk* lo = deallocChunk_;
272     Chunk* hi = deallocChunk_ + 1;
273     Chunk* loBound = &chunks_.front();
274     Chunk* hiBound = &chunks_.back() + 1;
275 
276     for (;;)
277     {
278         if (lo)
279         {
280             if (p >= lo->pData_ && p < lo->pData_ + chunkLength)
281             {
282                 return lo;
283             }
284             if (lo == loBound) lo = 0;
285             else --lo;
286         }
287         
288         if (hi)
289         {
290             if (p >= hi->pData_ && p < hi->pData_ + chunkLength)
291             {
292                 return hi;
293             }
294             if (++hi == hiBound) hi = 0;
295         }
296     }
297     assert(false);
298     return 0;
299 }
300 
301 ////////////////////////////////////////////////////////////////////////////////
302 // FixedAllocator::DoDeallocate (internal)
303 // Performs deallocation. Assumes deallocChunk_ points to the correct chunk
304 ////////////////////////////////////////////////////////////////////////////////
305 
306 void FixedAllocator::DoDeallocate(void* p)
307 {
308     assert(deallocChunk_->pData_ <= p);
309     assert(deallocChunk_->pData_ + numBlocks_ * blockSize_ > p);
310 
311     // call into the chunk, will adjust the inner list but won't release memory
312     deallocChunk_->Deallocate(p, blockSize_);
313 
314     if (deallocChunk_->blocksAvailable_ == numBlocks_)
315     {
316         // deallocChunk_ is completely free, should we release it? 
317         
318         Chunk& lastChunk = chunks_.back();
319         
320         if (&lastChunk == deallocChunk_)
321         {
322             // check if we have two last chunks empty
323             
324             if (chunks_.size() > 1 && 
325                 deallocChunk_[-1].blocksAvailable_ == numBlocks_)
326             {
327                 // Two free chunks, discard the last one
328                 lastChunk.Release();
329                 chunks_.pop_back();
330                 allocChunk_ = deallocChunk_ = &chunks_.front();
331             }
332             return;
333         }
334         
335         if (lastChunk.blocksAvailable_ == numBlocks_)
336         {
337             // Two free blocks, discard one
338             lastChunk.Release();
339             chunks_.pop_back();
340             allocChunk_ = deallocChunk_;
341         }
342         else
343         {
344             // move the empty chunk to the end
345             std::swap(*deallocChunk_, lastChunk);
346             allocChunk_ = &chunks_.back();
347         }
348     }
349 }
350 
351 ////////////////////////////////////////////////////////////////////////////////
352 // SmallObjAllocator::SmallObjAllocator
353 // Creates an allocator for small objects given chunk size and maximum 'small'
354 //     object size
355 ////////////////////////////////////////////////////////////////////////////////
356 
357 SmallObjAllocator::SmallObjAllocator(
358         std::size_t chunkSize, 
359         std::size_t maxObjectSize)
360     : pLastAlloc_(0), pLastDealloc_(0)
361     , chunkSize_(chunkSize), maxObjectSize_(maxObjectSize) 
362 {   
363 }
364 
365 ////////////////////////////////////////////////////////////////////////////////
366 // SmallObjAllocator::Allocate
367 // Allocates 'numBytes' memory
368 // Uses an internal pool of FixedAllocator objects for small objects  
369 ////////////////////////////////////////////////////////////////////////////////
370 
371 void* SmallObjAllocator::Allocate(std::size_t numBytes)
372 {
373     if (numBytes > maxObjectSize_) return operator new(numBytes);
374     
375     if (pLastAlloc_ && pLastAlloc_->BlockSize() == numBytes)
376     {
377         return pLastAlloc_->Allocate();
378     }
379     Pool::iterator i = std::lower_bound(pool_.begin(), pool_.end(), numBytes);
380     if (i == pool_.end() || i->BlockSize() != numBytes)
381     {
382         i = pool_.insert(i, FixedAllocator(numBytes));
383         pLastDealloc_ = &*pool_.begin();
384     }
385     pLastAlloc_ = &*i;
386     return pLastAlloc_->Allocate();
387 }
388 
389 ////////////////////////////////////////////////////////////////////////////////
390 // SmallObjAllocator::Deallocate
391 // Deallocates memory previously allocated with Allocate
392 // (undefined behavior if you pass any other pointer)
393 ////////////////////////////////////////////////////////////////////////////////
394 
395 void SmallObjAllocator::Deallocate(void* p, std::size_t numBytes)
396 {
397     if (numBytes > maxObjectSize_) return operator delete(p);
398 
399     if (pLastDealloc_ && pLastDealloc_->BlockSize() == numBytes)
400     {
401         pLastDealloc_->Deallocate(p);
402         return;
403     }
404     Pool::iterator i = std::lower_bound(pool_.begin(), pool_.end(), numBytes);
405     assert(i != pool_.end());
406     assert(i->BlockSize() == numBytes);
407     pLastDealloc_ = &*i;
408     pLastDealloc_->Deallocate(p);
409 }
410 
411 ////////////////////////////////////////////////////////////////////////////////
412 // Change log:
413 // March 20: fix exception safety issue in FixedAllocator::Allocate 
414 //     (thanks to Chris Udazvinis for pointing that out)
415 // June 20, 2001: ported by Nick Thurn to gcc 2.95.3. Kudos, Nick!!!
416 ////////////////////////////////////////////////////////////////////////////////
posted @ 2012-10-31 15:20  crazylhf  阅读(154)  评论(0)    收藏  举报