~ubuntu-branches/ubuntu/wily/tora/wily-proposed

« back to all changes in this revision

Viewing changes to ext/loki/loki-0.1.6/src/SmallObj.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Michael Meskes
  • Date: 2009-04-07 13:16:05 UTC
  • mfrom: (1.2.7 upstream) (3.1.3 sid)
  • Revision ID: james.westby@ubuntu.com-20090407131605-u422yigfv7jgg0l0
Tags: 2.0.0-3
* Cleaned up packaging a little bit.
* Added homepage information to control file.
* Bumped Standards-Version to 3.8.1.
* Released to unstable.

Show diffs side-by-side

added added

removed removed

Lines of Context:
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-Wesley 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
 
// $Id: SmallObj.cpp 756 2006-10-17 20:05:42Z syntheticpp $
17
 
 
18
 
 
19
 
#include <loki/SmallObj.h>
20
 
 
21
 
#include <cassert>
22
 
#include <vector>
23
 
#include <bitset>
24
 
 
25
 
//#define DO_EXTRA_LOKI_TESTS
26
 
//#define USE_NEW_TO_ALLOCATE
27
 
 
28
 
#ifdef DO_EXTRA_LOKI_TESTS
29
 
    #include <iostream>
30
 
#endif
31
 
 
32
 
namespace Loki
33
 
{
34
 
 
35
 
    /** @struct Chunk
36
 
        @ingroup SmallObjectGroupInternal
37
 
     Contains info about each allocated Chunk - which is a collection of
38
 
     contiguous blocks.  Each block is the same size, as specified by the
39
 
     FixedAllocator.  The number of blocks in a Chunk depends upon page size.
40
 
     This is a POD-style struct with value-semantics.  All functions and data
41
 
     are private so that they can not be changed by anything other than the
42
 
     FixedAllocator which owns the Chunk.
43
 
 
44
 
     @par Minimal Interface
45
 
     For the sake of runtime efficiency, no constructor, destructor, or
46
 
     copy-assignment operator is defined. The inline functions made by the
47
 
     compiler should be sufficient, and perhaps faster than hand-crafted
48
 
     functions.  The lack of these functions allows vector to create and copy
49
 
     Chunks as needed without overhead.  The Init and Release functions do
50
 
     what the default constructor and destructor would do.  A Chunk is not in
51
 
     a usable state after it is constructed and before calling Init.  Nor is
52
 
     a Chunk usable after Release is called, but before the destructor.
53
 
 
54
 
     @par Efficiency
55
 
     Down near the lowest level of the allocator, runtime efficiencies trump
56
 
     almost all other considerations.  Each function does the minimum required
57
 
     of it.  All functions should execute in constant time to prevent higher-
58
 
     level code from unwittingly using a version of Shlemiel the Painter's
59
 
     Algorithm.
60
 
 
61
 
     @par Stealth Indexes
62
 
     The first char of each empty block contains the index of the next empty
63
 
     block.  These stealth indexes form a singly-linked list within the blocks.
64
 
     A Chunk is corrupt if this singly-linked list has a loop or is shorter
65
 
     than blocksAvailable_.  Much of the allocator's time and space efficiency
66
 
     comes from how these stealth indexes are implemented.
67
 
     */
68
 
    class Chunk
69
 
    {
70
 
    private:
71
 
        friend class FixedAllocator;
72
 
 
73
 
        /** Initializes a just-constructed Chunk.
74
 
         @param blockSize Number of bytes per block.
75
 
         @param blocks Number of blocks per Chunk.
76
 
         @return True for success, false for failure.
77
 
         */
78
 
        bool Init( std::size_t blockSize, unsigned char blocks );
79
 
 
80
 
        /** Allocate a block within the Chunk.  Complexity is always O(1), and
81
 
         this will never throw.  Does not actually "allocate" by calling
82
 
         malloc, new, or any other function, but merely adjusts some internal
83
 
         indexes to indicate an already allocated block is no longer available.
84
 
         @return Pointer to block within Chunk.
85
 
         */
86
 
        void * Allocate( std::size_t blockSize );
87
 
 
88
 
        /** Deallocate a block within the Chunk. Complexity is always O(1), and
89
 
         this will never throw.  For efficiency, this assumes the address is
90
 
         within the block and aligned along the correct byte boundary.  An
91
 
         assertion checks the alignment, and a call to HasBlock is done from
92
 
         within VicinityFind.  Does not actually "deallocate" by calling free,
93
 
         delete, or other function, but merely adjusts some internal indexes to
94
 
         indicate a block is now available.
95
 
         */
96
 
        void Deallocate( void * p, std::size_t blockSize );
97
 
 
98
 
        /** Resets the Chunk back to pristine values. The available count is
99
 
         set back to zero, and the first available index is set to the zeroth
100
 
         block.  The stealth indexes inside each block are set to point to the
101
 
         next block. This assumes the Chunk's data was already using Init.
102
 
         */
103
 
        void Reset( std::size_t blockSize, unsigned char blocks );
104
 
 
105
 
        /// Releases the allocated block of memory.
106
 
        void Release();
107
 
 
108
 
        /** Determines if the Chunk has been corrupted.
109
 
         @param numBlocks Total # of blocks in the Chunk.
110
 
         @param blockSize # of bytes in each block.
111
 
         @param checkIndexes True if caller wants to check indexes of available
112
 
          blocks for corruption.  If false, then caller wants to skip some
113
 
          tests tests just to run faster.  (Debug version does more checks, but
114
 
          release version runs faster.)
115
 
         @return True if Chunk is corrupt.
116
 
         */
117
 
        bool IsCorrupt( unsigned char numBlocks, std::size_t blockSize,
118
 
            bool checkIndexes ) const;
119
 
 
120
 
        /** Determines if block is available.
121
 
         @param p Address of block managed by Chunk.
122
 
         @param numBlocks Total # of blocks in the Chunk.
123
 
         @param blockSize # of bytes in each block.
124
 
         @return True if block is available, else false if allocated.
125
 
         */
126
 
        bool IsBlockAvailable( void * p, unsigned char numBlocks,
127
 
            std::size_t blockSize ) const;
128
 
 
129
 
        /// Returns true if block at address P is inside this Chunk.
130
 
        inline bool HasBlock( void * p, std::size_t chunkLength ) const
131
 
        {
132
 
            unsigned char * pc = static_cast< unsigned char * >( p );
133
 
            return ( pData_ <= pc ) && ( pc < pData_ + chunkLength );
134
 
        }
135
 
 
136
 
        inline bool HasAvailable( unsigned char numBlocks ) const
137
 
        { return ( blocksAvailable_ == numBlocks ); }
138
 
 
139
 
        inline bool IsFilled( void ) const
140
 
        { return ( 0 == blocksAvailable_ ); }
141
 
 
142
 
        /// Pointer to array of allocated blocks.
143
 
        unsigned char * pData_;
144
 
        /// Index of first empty block.
145
 
        unsigned char firstAvailableBlock_;
146
 
        /// Count of empty blocks.
147
 
        unsigned char blocksAvailable_;
148
 
    };
149
 
 
150
 
    /** @class FixedAllocator
151
 
        @ingroup SmallObjectGroupInternal
152
 
     Offers services for allocating fixed-sized objects.  It has a container
153
 
     of "containers" of fixed-size blocks.  The outer container has all the
154
 
     Chunks.  The inner container is a Chunk which owns some blocks.
155
 
 
156
 
     @par Class Level Invariants
157
 
     - There is always either zero or one Chunk which is empty.
158
 
     - If this has no empty Chunk, then emptyChunk_ is NULL.
159
 
     - If this has an empty Chunk, then emptyChunk_ points to it.
160
 
     - If the Chunk container is empty, then deallocChunk_ and allocChunk_
161
 
       are NULL.
162
 
     - If the Chunk container is not-empty, then deallocChunk_ and allocChunk_
163
 
       are either NULL or point to Chunks within the container.
164
 
     - allocChunk_ will often point to the last Chunk in the container since
165
 
       it was likely allocated most recently, and therefore likely to have an
166
 
       available block.
167
 
     */
168
 
    class FixedAllocator
169
 
    {
170
 
    private:
171
 
 
172
 
        /** Deallocates the block at address p, and then handles the internal
173
 
         bookkeeping needed to maintain class invariants.  This assumes that
174
 
         deallocChunk_ points to the correct chunk.
175
 
         */
176
 
        void DoDeallocate( void * p );
177
 
 
178
 
        /** Creates an empty Chunk and adds it to the end of the ChunkList.
179
 
         All calls to the lower-level memory allocation functions occur inside
180
 
         this function, and so the only try-catch block is inside here.
181
 
         @return true for success, false for failure.
182
 
         */
183
 
        bool MakeNewChunk( void );
184
 
 
185
 
        /** Finds the Chunk which owns the block at address p.  It starts at
186
 
         deallocChunk_ and searches in both forwards and backwards directions
187
 
         from there until it finds the Chunk which owns p.  This algorithm
188
 
         should find the Chunk quickly if it is deallocChunk_ or is close to it
189
 
         in the Chunks container.  This goes both forwards and backwards since
190
 
         that works well for both same-order and opposite-order deallocations.
191
 
         (Same-order = objects are deallocated in the same order in which they
192
 
         were allocated.  Opposite order = objects are deallocated in a last to
193
 
         first order.  Complexity is O(C) where C is count of all Chunks.  This
194
 
         never throws.
195
 
         @return Pointer to Chunk that owns p, or NULL if no owner found.
196
 
         */
197
 
        Chunk * VicinityFind( void * p ) const;
198
 
 
199
 
        /// Not implemented.
200
 
        FixedAllocator(const FixedAllocator&);
201
 
        /// Not implemented.
202
 
        FixedAllocator& operator=(const FixedAllocator&);
203
 
 
204
 
        /// Type of container used to hold Chunks.
205
 
        typedef std::vector< Chunk > Chunks;
206
 
        /// Iterator through container of Chunks.
207
 
        typedef Chunks::iterator ChunkIter;
208
 
        /// Iterator through const container of Chunks.
209
 
        typedef Chunks::const_iterator ChunkCIter;
210
 
 
211
 
        /// Fewest # of objects managed by a Chunk.
212
 
        static unsigned char MinObjectsPerChunk_;
213
 
 
214
 
        /// Most # of objects managed by a Chunk - never exceeds UCHAR_MAX.
215
 
        static unsigned char MaxObjectsPerChunk_;
216
 
 
217
 
        /// Number of bytes in a single block within a Chunk.
218
 
        std::size_t blockSize_;
219
 
        /// Number of blocks managed by each Chunk.
220
 
        unsigned char numBlocks_;
221
 
 
222
 
        /// Container of Chunks.
223
 
        Chunks chunks_;
224
 
        /// Pointer to Chunk used for last or next allocation.
225
 
        Chunk * allocChunk_;
226
 
        /// Pointer to Chunk used for last or next deallocation.
227
 
        Chunk * deallocChunk_;
228
 
        /// Pointer to the only empty Chunk if there is one, else NULL.
229
 
        Chunk * emptyChunk_;
230
 
 
231
 
    public:
232
 
        /// Create a FixedAllocator which manages blocks of 'blockSize' size.
233
 
        FixedAllocator();
234
 
 
235
 
        /// Destroy the FixedAllocator and release all its Chunks.
236
 
        ~FixedAllocator();
237
 
 
238
 
        /// Initializes a FixedAllocator by calculating # of blocks per Chunk.
239
 
        void Initialize( std::size_t blockSize, std::size_t pageSize );
240
 
 
241
 
        /** Returns pointer to allocated memory block of fixed size - or NULL
242
 
         if it failed to allocate.
243
 
         */
244
 
        void * Allocate( void );
245
 
 
246
 
        /** Deallocate a memory block previously allocated with Allocate.  If
247
 
         the block is not owned by this FixedAllocator, it returns false so
248
 
         that SmallObjAllocator can call the default deallocator.  If the
249
 
         block was found, this returns true.
250
 
         */
251
 
        bool Deallocate( void * p, Chunk * hint );
252
 
 
253
 
        /// Returns block size with which the FixedAllocator was initialized.
254
 
        inline std::size_t BlockSize() const { return blockSize_; }
255
 
 
256
 
        /** Releases the memory used by the empty Chunk.  This will take
257
 
         constant time under any situation.
258
 
         @return True if empty chunk found and released, false if none empty.
259
 
         */
260
 
        bool TrimEmptyChunk( void );
261
 
 
262
 
        /** Releases unused spots from ChunkList.  This takes constant time
263
 
         with respect to # of Chunks, but actual time depends on underlying
264
 
         memory allocator.
265
 
         @return False if no unused spots, true if some found and released.
266
 
         */
267
 
        bool TrimChunkList( void );
268
 
 
269
 
        /** Returns count of empty Chunks held by this allocator.  Complexity
270
 
         is O(C) where C is the total number of Chunks - empty or used.
271
 
         */
272
 
        std::size_t CountEmptyChunks( void ) const;
273
 
 
274
 
        /** Determines if FixedAllocator is corrupt.  Checks data members to
275
 
         see if any have erroneous values, or violate class invariants.  It
276
 
         also checks if any Chunk is corrupt.  Complexity is O(C) where C is
277
 
         the number of Chunks.  If any data is corrupt, this will return true
278
 
         in release mode, or assert in debug mode.
279
 
         */
280
 
        bool IsCorrupt( void ) const;
281
 
 
282
 
        /** Returns true if the block at address p is within a Chunk owned by
283
 
         this FixedAllocator.  Complexity is O(C) where C is the total number
284
 
         of Chunks - empty or used.
285
 
         */
286
 
        const Chunk * HasBlock( void * p ) const;
287
 
        inline Chunk * HasBlock( void * p )
288
 
        {
289
 
            return const_cast< Chunk * >(
290
 
                const_cast< const FixedAllocator * >( this )->HasBlock( p ) );
291
 
        }
292
 
 
293
 
    };
294
 
 
295
 
    unsigned char FixedAllocator::MinObjectsPerChunk_ = 8;
296
 
    unsigned char FixedAllocator::MaxObjectsPerChunk_ = UCHAR_MAX;
297
 
 
298
 
// Chunk::Init ----------------------------------------------------------------
299
 
 
300
 
bool Chunk::Init( std::size_t blockSize, unsigned char blocks )
301
 
{
302
 
    assert(blockSize > 0);
303
 
    assert(blocks > 0);
304
 
    // Overflow check
305
 
    const std::size_t allocSize = blockSize * blocks;
306
 
    assert( allocSize / blockSize == blocks);
307
 
 
308
 
#ifdef USE_NEW_TO_ALLOCATE
309
 
    // If this new operator fails, it will throw, and the exception will get
310
 
    // caught one layer up.
311
 
    pData_ = static_cast< unsigned char * >( ::operator new ( allocSize ) );
312
 
#else
313
 
    // malloc can't throw, so its only way to indicate an error is to return
314
 
    // a NULL pointer, so we have to check for that.
315
 
    pData_ = static_cast< unsigned char * >( ::std::malloc( allocSize ) );
316
 
    if ( NULL == pData_ ) return false;
317
 
#endif
318
 
 
319
 
    Reset( blockSize, blocks );
320
 
    return true;
321
 
}
322
 
 
323
 
// Chunk::Reset ---------------------------------------------------------------
324
 
 
325
 
void Chunk::Reset(std::size_t blockSize, unsigned char blocks)
326
 
{
327
 
    assert(blockSize > 0);
328
 
    assert(blocks > 0);
329
 
    // Overflow check
330
 
    assert((blockSize * blocks) / blockSize == blocks);
331
 
 
332
 
    firstAvailableBlock_ = 0;
333
 
    blocksAvailable_ = blocks;
334
 
 
335
 
    unsigned char i = 0;
336
 
    for ( unsigned char * p = pData_; i != blocks; p += blockSize )
337
 
    {
338
 
        *p = ++i;
339
 
    }
340
 
}
341
 
 
342
 
// Chunk::Release -------------------------------------------------------------
343
 
 
344
 
void Chunk::Release()
345
 
{
346
 
    assert( NULL != pData_ );
347
 
#ifdef USE_NEW_TO_ALLOCATE
348
 
    ::operator delete ( pData_ );
349
 
#else
350
 
    ::std::free( static_cast< void * >( pData_ ) );
351
 
#endif
352
 
}
353
 
 
354
 
// Chunk::Allocate ------------------------------------------------------------
355
 
 
356
 
void* Chunk::Allocate(std::size_t blockSize)
357
 
{
358
 
    if ( IsFilled() ) return NULL;
359
 
 
360
 
    assert((firstAvailableBlock_ * blockSize) / blockSize == 
361
 
        firstAvailableBlock_);
362
 
    unsigned char * pResult = pData_ + (firstAvailableBlock_ * blockSize);
363
 
    firstAvailableBlock_ = *pResult;
364
 
    --blocksAvailable_;
365
 
 
366
 
    return pResult;
367
 
}
368
 
 
369
 
// Chunk::Deallocate ----------------------------------------------------------
370
 
 
371
 
void Chunk::Deallocate(void* p, std::size_t blockSize)
372
 
{
373
 
    assert(p >= pData_);
374
 
 
375
 
    unsigned char* toRelease = static_cast<unsigned char*>(p);
376
 
    // Alignment check
377
 
    assert((toRelease - pData_) % blockSize == 0);
378
 
    unsigned char index = static_cast< unsigned char >(
379
 
        ( toRelease - pData_ ) / blockSize);
380
 
 
381
 
#if defined(DEBUG) || defined(_DEBUG)
382
 
    // Check if block was already deleted.  Attempting to delete the same
383
 
    // block more than once causes Chunk's linked-list of stealth indexes to
384
 
    // become corrupt.  And causes count of blocksAvailable_ to be wrong.
385
 
    if ( 0 < blocksAvailable_ )
386
 
        assert( firstAvailableBlock_ != index );
387
 
#endif
388
 
 
389
 
    *toRelease = firstAvailableBlock_;
390
 
    firstAvailableBlock_ = index;
391
 
    // Truncation check
392
 
    assert(firstAvailableBlock_ == (toRelease - pData_) / blockSize);
393
 
 
394
 
    ++blocksAvailable_;
395
 
}
396
 
 
397
 
// Chunk::IsCorrupt -----------------------------------------------------------
398
 
 
399
 
bool Chunk::IsCorrupt( unsigned char numBlocks, std::size_t blockSize,
400
 
    bool checkIndexes ) const
401
 
{
402
 
 
403
 
    if ( numBlocks < blocksAvailable_ )
404
 
    {
405
 
        // Contents at this Chunk corrupted.  This might mean something has
406
 
        // overwritten memory owned by the Chunks container.
407
 
        assert( false );
408
 
        return true;
409
 
    }
410
 
    if ( IsFilled() )
411
 
        // Useless to do further corruption checks if all blocks allocated.
412
 
        return false;
413
 
    unsigned char index = firstAvailableBlock_;
414
 
    if ( numBlocks <= index )
415
 
    {
416
 
        // Contents at this Chunk corrupted.  This might mean something has
417
 
        // overwritten memory owned by the Chunks container.
418
 
        assert( false );
419
 
        return true;
420
 
    }
421
 
    if ( !checkIndexes )
422
 
        // Caller chose to skip more complex corruption tests.
423
 
        return false;
424
 
 
425
 
    /* If the bit at index was set in foundBlocks, then the stealth index was
426
 
     found on the linked-list.
427
 
     */
428
 
    std::bitset< UCHAR_MAX > foundBlocks;
429
 
    unsigned char * nextBlock = NULL;
430
 
 
431
 
    /* The loop goes along singly linked-list of stealth indexes and makes sure
432
 
     that each index is within bounds (0 <= index < numBlocks) and that the
433
 
     index was not already found while traversing the linked-list.  The linked-
434
 
     list should have exactly blocksAvailable_ nodes, so the for loop will not
435
 
     check more than blocksAvailable_.  This loop can't check inside allocated
436
 
     blocks for corruption since such blocks are not within the linked-list.
437
 
     Contents of allocated blocks are not changed by Chunk.
438
 
 
439
 
     Here are the types of corrupted link-lists which can be verified.  The
440
 
     corrupt index is shown with asterisks in each example.
441
 
 
442
 
     Type 1: Index is too big.
443
 
      numBlocks == 64
444
 
      blocksAvailable_ == 7
445
 
      firstAvailableBlock_ -> 17 -> 29 -> *101*
446
 
      There should be no indexes which are equal to or larger than the total
447
 
      number of blocks.  Such an index would refer to a block beyond the
448
 
      Chunk's allocated domain.
449
 
 
450
 
     Type 2: Index is repeated.
451
 
      numBlocks == 64
452
 
      blocksAvailable_ == 5
453
 
      firstAvailableBlock_ -> 17 -> 29 -> 53 -> *17* -> 29 -> 53 ...
454
 
      No index should be repeated within the linked-list since that would
455
 
      indicate the presence of a loop in the linked-list.
456
 
     */
457
 
    for ( unsigned char cc = 0; ; )
458
 
    {
459
 
        nextBlock = pData_ + ( index * blockSize );
460
 
        foundBlocks.set( index, true );
461
 
        ++cc;
462
 
        if ( cc >= blocksAvailable_ )
463
 
            // Successfully counted off number of nodes in linked-list.
464
 
            break;
465
 
        index = *nextBlock;
466
 
        if ( numBlocks <= index )
467
 
        {
468
 
            /* This catches Type 1 corruptions as shown in above comments.
469
 
             This implies that a block was corrupted due to a stray pointer
470
 
             or an operation on a nearby block overran the size of the block.
471
 
             */
472
 
            assert( false );
473
 
            return true;
474
 
        }
475
 
        if ( foundBlocks.test( index ) )
476
 
        {
477
 
            /* This catches Type 2 corruptions as shown in above comments.
478
 
             This implies that a block was corrupted due to a stray pointer
479
 
             or an operation on a nearby block overran the size of the block.
480
 
             Or perhaps the program tried to delete a block more than once.
481
 
             */
482
 
            assert( false );
483
 
            return true;
484
 
        }
485
 
    }
486
 
    if ( foundBlocks.count() != blocksAvailable_ )
487
 
    {
488
 
        /* This implies that the singly-linked-list of stealth indexes was
489
 
         corrupted.  Ideally, this should have been detected within the loop.
490
 
         */
491
 
        assert( false );
492
 
        return true;
493
 
    }
494
 
 
495
 
    return false;
496
 
}
497
 
 
498
 
// Chunk::IsBlockAvailable ----------------------------------------------------
499
 
 
500
 
bool Chunk::IsBlockAvailable( void * p, unsigned char numBlocks,
501
 
    std::size_t blockSize ) const
502
 
{
503
 
    (void) numBlocks;
504
 
    
505
 
    if ( IsFilled() )
506
 
        return false;
507
 
 
508
 
    unsigned char * place = static_cast< unsigned char * >( p );
509
 
    // Alignment check
510
 
    assert( ( place - pData_ ) % blockSize == 0 );
511
 
    unsigned char blockIndex = static_cast< unsigned char >(
512
 
        ( place - pData_ ) / blockSize );
513
 
 
514
 
    unsigned char index = firstAvailableBlock_;
515
 
    assert( numBlocks > index );
516
 
    if ( index == blockIndex )
517
 
        return true;
518
 
 
519
 
    /* If the bit at index was set in foundBlocks, then the stealth index was
520
 
     found on the linked-list.
521
 
     */
522
 
    std::bitset< UCHAR_MAX > foundBlocks;
523
 
    unsigned char * nextBlock = NULL;
524
 
    for ( unsigned char cc = 0; ; )
525
 
    {
526
 
        nextBlock = pData_ + ( index * blockSize );
527
 
        foundBlocks.set( index, true );
528
 
        ++cc;
529
 
        if ( cc >= blocksAvailable_ )
530
 
            // Successfully counted off number of nodes in linked-list.
531
 
            break;
532
 
        index = *nextBlock;
533
 
        if ( index == blockIndex )
534
 
            return true;
535
 
        assert( numBlocks > index );
536
 
        assert( !foundBlocks.test( index ) );
537
 
    }
538
 
 
539
 
    return false;
540
 
}
541
 
 
542
 
// FixedAllocator::FixedAllocator ---------------------------------------------
543
 
 
544
 
FixedAllocator::FixedAllocator()
545
 
    : blockSize_( 0 )
546
 
    , numBlocks_( 0 )
547
 
    , chunks_( 0 )
548
 
    , allocChunk_( NULL )
549
 
    , deallocChunk_( NULL )
550
 
    , emptyChunk_( NULL )
551
 
{
552
 
}
553
 
 
554
 
// FixedAllocator::~FixedAllocator --------------------------------------------
555
 
 
556
 
FixedAllocator::~FixedAllocator()
557
 
{
558
 
#ifdef DO_EXTRA_LOKI_TESTS
559
 
    TrimEmptyChunk();
560
 
    assert( chunks_.empty() && "Memory leak detected!" );
561
 
#endif
562
 
    for ( ChunkIter i( chunks_.begin() ); i != chunks_.end(); ++i )
563
 
       i->Release();
564
 
}
565
 
 
566
 
// FixedAllocator::Initialize -------------------------------------------------
567
 
 
568
 
void FixedAllocator::Initialize( std::size_t blockSize, std::size_t pageSize )
569
 
{
570
 
    assert( blockSize > 0 );
571
 
    assert( pageSize >= blockSize );
572
 
    blockSize_ = blockSize;
573
 
 
574
 
    std::size_t numBlocks = pageSize / blockSize;
575
 
    if ( numBlocks > MaxObjectsPerChunk_ ) numBlocks = MaxObjectsPerChunk_;
576
 
    else if ( numBlocks < MinObjectsPerChunk_ ) numBlocks = MinObjectsPerChunk_;
577
 
 
578
 
    numBlocks_ = static_cast<unsigned char>(numBlocks);
579
 
    assert(numBlocks_ == numBlocks);
580
 
}
581
 
 
582
 
// FixedAllocator::CountEmptyChunks -------------------------------------------
583
 
 
584
 
std::size_t FixedAllocator::CountEmptyChunks( void ) const
585
 
{
586
 
#ifdef DO_EXTRA_LOKI_TESTS
587
 
    // This code is only used for specialized tests of the allocator.
588
 
    // It is #ifdef-ed so that its O(C) complexity does not overwhelm the
589
 
    // functions which call it.
590
 
    std::size_t count = 0;
591
 
    for ( ChunkCIter it( chunks_.begin() ); it != chunks_.end(); ++it )
592
 
    {
593
 
        const Chunk & chunk = *it;
594
 
        if ( chunk.HasAvailable( numBlocks_ ) )
595
 
            ++count;
596
 
    }
597
 
    return count;
598
 
#else
599
 
    return ( NULL == emptyChunk_ ) ? 0 : 1;
600
 
#endif
601
 
}
602
 
 
603
 
// FixedAllocator::IsCorrupt --------------------------------------------------
604
 
 
605
 
bool FixedAllocator::IsCorrupt( void ) const
606
 
{
607
 
    const bool isEmpty = chunks_.empty();
608
 
    ChunkCIter start( chunks_.begin() );
609
 
    ChunkCIter last( chunks_.end() );
610
 
    const size_t emptyChunkCount = CountEmptyChunks();
611
 
 
612
 
    if ( isEmpty )
613
 
    {
614
 
        if ( start != last )
615
 
        {
616
 
            assert( false );
617
 
            return true;
618
 
        }
619
 
        if ( 0 < emptyChunkCount )
620
 
        {
621
 
            assert( false );
622
 
            return true;
623
 
        }
624
 
        if ( NULL != deallocChunk_ )
625
 
        {
626
 
            assert( false );
627
 
            return true;
628
 
        }
629
 
        if ( NULL != allocChunk_ )
630
 
        {
631
 
            assert( false );
632
 
            return true;
633
 
        }
634
 
        if ( NULL != emptyChunk_ )
635
 
        {
636
 
            assert( false );
637
 
            return true;
638
 
        }
639
 
    }
640
 
 
641
 
    else
642
 
    {
643
 
        const Chunk * front = &chunks_.front();
644
 
        const Chunk * back  = &chunks_.back();
645
 
        if ( start >= last )
646
 
        {
647
 
            assert( false );
648
 
            return true;
649
 
        }
650
 
        if ( back < deallocChunk_ )
651
 
        {
652
 
            assert( false );
653
 
            return true;
654
 
        }
655
 
        if ( back < allocChunk_ )
656
 
        {
657
 
            assert( false );
658
 
            return true;
659
 
        }
660
 
        if ( front > deallocChunk_ )
661
 
        {
662
 
            assert( false );
663
 
            return true;
664
 
        }
665
 
        if ( front > allocChunk_ )
666
 
        {
667
 
            assert( false );
668
 
            return true;
669
 
        }
670
 
 
671
 
        switch ( emptyChunkCount )
672
 
        {
673
 
            case 0:
674
 
                if ( emptyChunk_ != NULL )
675
 
                {
676
 
                    assert( false );
677
 
                    return true;
678
 
                }
679
 
                break;
680
 
            case 1:
681
 
                if ( emptyChunk_ == NULL )
682
 
                {
683
 
                    assert( false );
684
 
                    return true;
685
 
                }
686
 
                if ( back < emptyChunk_ )
687
 
                {
688
 
                    assert( false );
689
 
                    return true;
690
 
                }
691
 
                if ( front > emptyChunk_ )
692
 
                {
693
 
                    assert( false );
694
 
                    return true;
695
 
                }
696
 
                if ( !emptyChunk_->HasAvailable( numBlocks_ ) )
697
 
                {
698
 
                    // This may imply somebody tried to delete a block twice.
699
 
                    assert( false );
700
 
                    return true;
701
 
                }
702
 
                break;
703
 
            default:
704
 
                assert( false );
705
 
                return true;
706
 
        }
707
 
        for ( ChunkCIter it( start ); it != last; ++it )
708
 
        {
709
 
            const Chunk & chunk = *it;
710
 
            if ( chunk.IsCorrupt( numBlocks_, blockSize_, true ) )
711
 
                return true;
712
 
        }
713
 
    }
714
 
 
715
 
    return false;
716
 
}
717
 
 
718
 
// FixedAllocator::HasBlock ---------------------------------------------------
719
 
 
720
 
const Chunk * FixedAllocator::HasBlock( void * p ) const
721
 
{
722
 
    const std::size_t chunkLength = numBlocks_ * blockSize_;
723
 
    for ( ChunkCIter it( chunks_.begin() ); it != chunks_.end(); ++it )
724
 
    {
725
 
        const Chunk & chunk = *it;
726
 
        if ( chunk.HasBlock( p, chunkLength ) )
727
 
            return &chunk;
728
 
    }
729
 
    return NULL;
730
 
}
731
 
 
732
 
// FixedAllocator::TrimEmptyChunk ---------------------------------------------
733
 
 
734
 
bool FixedAllocator::TrimEmptyChunk( void )
735
 
{
736
 
    // prove either emptyChunk_ points nowhere, or points to a truly empty Chunk.
737
 
    assert( ( NULL == emptyChunk_ ) || ( emptyChunk_->HasAvailable( numBlocks_ ) ) );
738
 
    if ( NULL == emptyChunk_ ) return false;
739
 
 
740
 
    // If emptyChunk_ points to valid Chunk, then chunk list is not empty.
741
 
    assert( !chunks_.empty() );
742
 
    // And there should be exactly 1 empty Chunk.
743
 
    assert( 1 == CountEmptyChunks() );
744
 
 
745
 
    Chunk * lastChunk = &chunks_.back();
746
 
    if ( lastChunk != emptyChunk_ )
747
 
        std::swap( *emptyChunk_, *lastChunk );
748
 
    assert( lastChunk->HasAvailable( numBlocks_ ) );
749
 
    lastChunk->Release();
750
 
    chunks_.pop_back();
751
 
 
752
 
    if ( chunks_.empty() )
753
 
    {
754
 
        allocChunk_ = NULL;
755
 
        deallocChunk_ = NULL;
756
 
    }
757
 
    else
758
 
    {
759
 
        if ( deallocChunk_ == emptyChunk_ )
760
 
        {
761
 
            deallocChunk_ = &chunks_.front();
762
 
            assert( deallocChunk_->blocksAvailable_ < numBlocks_ );
763
 
        }
764
 
        if ( allocChunk_ == emptyChunk_ )
765
 
        {
766
 
            allocChunk_ = &chunks_.back();
767
 
            assert( allocChunk_->blocksAvailable_ < numBlocks_ );
768
 
        }
769
 
    }
770
 
 
771
 
    emptyChunk_ = NULL;
772
 
    assert( 0 == CountEmptyChunks() );
773
 
 
774
 
    return true;
775
 
}
776
 
 
777
 
// FixedAllocator::TrimChunkList ----------------------------------------------
778
 
 
779
 
bool FixedAllocator::TrimChunkList( void )
780
 
{
781
 
    if ( chunks_.empty() )
782
 
    {
783
 
        assert( NULL == allocChunk_ );
784
 
        assert( NULL == deallocChunk_ );
785
 
    }
786
 
 
787
 
    if ( chunks_.size() == chunks_.capacity() )
788
 
        return false;
789
 
    // Use the "make-a-temp-and-swap" trick to remove excess capacity.
790
 
    Chunks( chunks_ ).swap( chunks_ );
791
 
 
792
 
    return true;
793
 
}
794
 
 
795
 
// FixedAllocator::MakeNewChunk -----------------------------------------------
796
 
 
797
 
bool FixedAllocator::MakeNewChunk( void )
798
 
{
799
 
    bool allocated = false;
800
 
    try
801
 
    {
802
 
        std::size_t size = chunks_.size();
803
 
        // Calling chunks_.reserve *before* creating and initializing the new
804
 
        // Chunk means that nothing is leaked by this function in case an
805
 
        // exception is thrown from reserve.
806
 
        if ( chunks_.capacity() == size )
807
 
        {
808
 
            if ( 0 == size ) size = 4;
809
 
            chunks_.reserve( size * 2 );
810
 
        }
811
 
        Chunk newChunk;
812
 
        allocated = newChunk.Init( blockSize_, numBlocks_ );
813
 
        if ( allocated )
814
 
            chunks_.push_back( newChunk );
815
 
    }
816
 
    catch ( ... )
817
 
    {
818
 
        allocated = false;
819
 
    }
820
 
    if ( !allocated ) return false;
821
 
 
822
 
    allocChunk_ = &chunks_.back();
823
 
    deallocChunk_ = &chunks_.front();
824
 
    return true;
825
 
}
826
 
 
827
 
// FixedAllocator::Allocate ---------------------------------------------------
828
 
 
829
 
void * FixedAllocator::Allocate( void )
830
 
{
831
 
    // prove either emptyChunk_ points nowhere, or points to a truly empty Chunk.
832
 
    assert( ( NULL == emptyChunk_ ) || ( emptyChunk_->HasAvailable( numBlocks_ ) ) );
833
 
    assert( CountEmptyChunks() < 2 );
834
 
 
835
 
    if ( ( NULL == allocChunk_ ) || allocChunk_->IsFilled() )
836
 
    {
837
 
        if ( NULL != emptyChunk_ )
838
 
        {
839
 
            allocChunk_ = emptyChunk_;
840
 
            emptyChunk_ = NULL;
841
 
        }
842
 
        else
843
 
        {
844
 
            for ( ChunkIter i( chunks_.begin() ); ; ++i )
845
 
            {
846
 
                if ( chunks_.end() == i )
847
 
                {
848
 
                    if ( !MakeNewChunk() )
849
 
                        return NULL;
850
 
                    break;
851
 
                }
852
 
                if ( !i->IsFilled() )
853
 
                {
854
 
                    allocChunk_ = &*i;
855
 
                    break;
856
 
                }
857
 
            }
858
 
        }
859
 
    }
860
 
    else if ( allocChunk_ == emptyChunk_)
861
 
        // detach emptyChunk_ from allocChunk_, because after 
862
 
        // calling allocChunk_->Allocate(blockSize_); the chunk 
863
 
        // is no longer empty.
864
 
        emptyChunk_ = NULL;
865
 
 
866
 
    assert( allocChunk_ != NULL );
867
 
    assert( !allocChunk_->IsFilled() );
868
 
    void * place = allocChunk_->Allocate( blockSize_ );
869
 
 
870
 
    // prove either emptyChunk_ points nowhere, or points to a truly empty Chunk.
871
 
    assert( ( NULL == emptyChunk_ ) || ( emptyChunk_->HasAvailable( numBlocks_ ) ) );
872
 
    assert( CountEmptyChunks() < 2 );
873
 
 
874
 
    return place;
875
 
}
876
 
 
877
 
// FixedAllocator::Deallocate -------------------------------------------------
878
 
 
879
 
bool FixedAllocator::Deallocate( void * p, Chunk * hint )
880
 
{
881
 
    assert(!chunks_.empty());
882
 
    assert(&chunks_.front() <= deallocChunk_);
883
 
    assert(&chunks_.back() >= deallocChunk_);
884
 
    assert( &chunks_.front() <= allocChunk_ );
885
 
    assert( &chunks_.back() >= allocChunk_ );
886
 
    assert( CountEmptyChunks() < 2 );
887
 
 
888
 
    Chunk * foundChunk = ( NULL == hint ) ? VicinityFind( p ) : hint;
889
 
    if ( NULL == foundChunk )
890
 
        return false;
891
 
 
892
 
    assert( foundChunk->HasBlock( p, numBlocks_ * blockSize_ ) );
893
 
#ifdef LOKI_CHECK_FOR_CORRUPTION
894
 
    if ( foundChunk->IsCorrupt( numBlocks_, blockSize_, true ) )
895
 
    {
896
 
        assert( false );
897
 
        return false;
898
 
    }
899
 
    if ( foundChunk->IsBlockAvailable( p, numBlocks_, blockSize_ ) )
900
 
    {
901
 
        assert( false );
902
 
        return false;
903
 
    }
904
 
#endif
905
 
    deallocChunk_ = foundChunk;
906
 
    DoDeallocate(p);
907
 
    assert( CountEmptyChunks() < 2 );
908
 
 
909
 
    return true;
910
 
}
911
 
 
912
 
// FixedAllocator::VicinityFind -----------------------------------------------
913
 
 
914
 
Chunk * FixedAllocator::VicinityFind( void * p ) const
915
 
{
916
 
    if ( chunks_.empty() ) return NULL;
917
 
    assert(deallocChunk_);
918
 
 
919
 
    const std::size_t chunkLength = numBlocks_ * blockSize_;
920
 
    Chunk * lo = deallocChunk_;
921
 
    Chunk * hi = deallocChunk_ + 1;
922
 
    const Chunk * loBound = &chunks_.front();
923
 
    const Chunk * hiBound = &chunks_.back() + 1;
924
 
 
925
 
    // Special case: deallocChunk_ is the last in the array
926
 
    if (hi == hiBound) hi = NULL;
927
 
 
928
 
    for (;;)
929
 
    {
930
 
        if (lo)
931
 
        {
932
 
            if ( lo->HasBlock( p, chunkLength ) ) return lo;
933
 
            if ( lo == loBound )
934
 
            {
935
 
                lo = NULL;
936
 
                if ( NULL == hi ) break;
937
 
            }
938
 
            else --lo;
939
 
        }
940
 
 
941
 
        if (hi)
942
 
        {
943
 
            if ( hi->HasBlock( p, chunkLength ) ) return hi;
944
 
            if ( ++hi == hiBound )
945
 
            {
946
 
                hi = NULL;
947
 
                if ( NULL == lo ) break;
948
 
            }
949
 
        }
950
 
    }
951
 
 
952
 
    return NULL;
953
 
}
954
 
 
955
 
// FixedAllocator::DoDeallocate -----------------------------------------------
956
 
 
957
 
void FixedAllocator::DoDeallocate(void* p)
958
 
{
959
 
    // Show that deallocChunk_ really owns the block at address p.
960
 
    assert( deallocChunk_->HasBlock( p, numBlocks_ * blockSize_ ) );
961
 
    // Either of the next two assertions may fail if somebody tries to
962
 
    // delete the same block twice.
963
 
    assert( emptyChunk_ != deallocChunk_ );
964
 
    assert( !deallocChunk_->HasAvailable( numBlocks_ ) );
965
 
    // prove either emptyChunk_ points nowhere, or points to a truly empty Chunk.
966
 
    assert( ( NULL == emptyChunk_ ) || ( emptyChunk_->HasAvailable( numBlocks_ ) ) );
967
 
 
968
 
    // call into the chunk, will adjust the inner list but won't release memory
969
 
    deallocChunk_->Deallocate(p, blockSize_);
970
 
 
971
 
    if ( deallocChunk_->HasAvailable( numBlocks_ ) )
972
 
    {
973
 
        assert( emptyChunk_ != deallocChunk_ );
974
 
        // deallocChunk_ is empty, but a Chunk is only released if there are 2
975
 
        // empty chunks.  Since emptyChunk_ may only point to a previously
976
 
        // cleared Chunk, if it points to something else besides deallocChunk_,
977
 
        // then FixedAllocator currently has 2 empty Chunks.
978
 
        if ( NULL != emptyChunk_ )
979
 
        {
980
 
            // If last Chunk is empty, just change what deallocChunk_
981
 
            // points to, and release the last.  Otherwise, swap an empty
982
 
            // Chunk with the last, and then release it.
983
 
            Chunk * lastChunk = &chunks_.back();
984
 
            if ( lastChunk == deallocChunk_ )
985
 
                deallocChunk_ = emptyChunk_;
986
 
            else if ( lastChunk != emptyChunk_ )
987
 
                std::swap( *emptyChunk_, *lastChunk );
988
 
            assert( lastChunk->HasAvailable( numBlocks_ ) );
989
 
            lastChunk->Release();
990
 
            chunks_.pop_back();
991
 
            if ( ( allocChunk_ == lastChunk ) || allocChunk_->IsFilled() ) 
992
 
                allocChunk_ = deallocChunk_;
993
 
        }
994
 
        emptyChunk_ = deallocChunk_;
995
 
    }
996
 
 
997
 
    // prove either emptyChunk_ points nowhere, or points to a truly empty Chunk.
998
 
    assert( ( NULL == emptyChunk_ ) || ( emptyChunk_->HasAvailable( numBlocks_ ) ) );
999
 
}
1000
 
 
1001
 
// GetOffset ------------------------------------------------------------------
1002
 
/// @ingroup SmallObjectGroupInternal
1003
 
/// Calculates index into array where a FixedAllocator of numBytes is located.
1004
 
inline std::size_t GetOffset( std::size_t numBytes, std::size_t alignment )
1005
 
{
1006
 
    const std::size_t alignExtra = alignment-1;
1007
 
    return ( numBytes + alignExtra ) / alignment;
1008
 
}
1009
 
 
1010
 
// DefaultAllocator -----------------------------------------------------------
1011
 
/** @ingroup SmallObjectGroupInternal
1012
 
 Calls the default allocator when SmallObjAllocator decides not to handle a
1013
 
 request.  SmallObjAllocator calls this if the number of bytes is bigger than
1014
 
 the size which can be handled by any FixedAllocator.
1015
 
 @param numBytes number of bytes
1016
 
 @param doThrow True if this function should throw an exception, or false if it
1017
 
  should indicate failure by returning a NULL pointer.
1018
 
*/
1019
 
void * DefaultAllocator( std::size_t numBytes, bool doThrow )
1020
 
{
1021
 
#ifdef USE_NEW_TO_ALLOCATE
1022
 
    return doThrow ? ::operator new( numBytes ) :
1023
 
        ::operator new( numBytes, std::nothrow_t() );
1024
 
#else
1025
 
    void * p = ::std::malloc( numBytes );
1026
 
    if ( doThrow && ( NULL == p ) )
1027
 
        throw std::bad_alloc();
1028
 
    return p;
1029
 
#endif
1030
 
}
1031
 
 
1032
 
// DefaultDeallocator ---------------------------------------------------------
1033
 
/** @ingroup SmallObjectGroupInternal
1034
 
 Calls default deallocator when SmallObjAllocator decides not to handle a   
1035
 
 request.  The default deallocator could be the global delete operator or the
1036
 
 free function.  The free function is the preferred default deallocator since
1037
 
 it matches malloc which is the preferred default allocator.  SmallObjAllocator
1038
 
 will call this if an address was not found among any of its own blocks.
1039
 
 */
1040
 
void DefaultDeallocator( void * p )
1041
 
{
1042
 
#ifdef USE_NEW_TO_ALLOCATE
1043
 
    ::operator delete( p );
1044
 
#else
1045
 
    ::std::free( p );
1046
 
#endif
1047
 
}
1048
 
 
1049
 
// SmallObjAllocator::SmallObjAllocator ---------------------------------------
1050
 
 
1051
 
SmallObjAllocator::SmallObjAllocator( std::size_t pageSize,
1052
 
    std::size_t maxObjectSize, std::size_t objectAlignSize ) :
1053
 
    pool_( NULL ),
1054
 
    maxSmallObjectSize_( maxObjectSize ),
1055
 
    objectAlignSize_( objectAlignSize )
1056
 
{
1057
 
#ifdef DO_EXTRA_LOKI_TESTS
1058
 
    std::cout << "SmallObjAllocator " << this << std::endl;
1059
 
#endif
1060
 
    assert( 0 != objectAlignSize );
1061
 
    const std::size_t allocCount = GetOffset( maxObjectSize, objectAlignSize );
1062
 
    pool_ = new FixedAllocator[ allocCount ];
1063
 
    for ( std::size_t i = 0; i < allocCount; ++i )
1064
 
        pool_[ i ].Initialize( ( i+1 ) * objectAlignSize, pageSize );
1065
 
}
1066
 
 
1067
 
// SmallObjAllocator::~SmallObjAllocator --------------------------------------
1068
 
 
1069
 
SmallObjAllocator::~SmallObjAllocator( void )
1070
 
{
1071
 
#ifdef DO_EXTRA_LOKI_TESTS
1072
 
    std::cout << "~SmallObjAllocator " << this << std::endl;
1073
 
#endif
1074
 
    delete [] pool_;
1075
 
}
1076
 
 
1077
 
// SmallObjAllocator::TrimExcessMemory ----------------------------------------
1078
 
 
1079
 
bool SmallObjAllocator::TrimExcessMemory( void )
1080
 
{
1081
 
    bool found = false;
1082
 
    const std::size_t allocCount = GetOffset( GetMaxObjectSize(), GetAlignment() );
1083
 
    std::size_t i = 0;
1084
 
    for ( ; i < allocCount; ++i )
1085
 
    {
1086
 
        if ( pool_[ i ].TrimEmptyChunk() )
1087
 
            found = true;
1088
 
    }
1089
 
    for ( i = 0; i < allocCount; ++i )
1090
 
    {
1091
 
        if ( pool_[ i ].TrimChunkList() )
1092
 
            found = true;
1093
 
    }
1094
 
 
1095
 
    return found;
1096
 
}
1097
 
 
1098
 
// SmallObjAllocator::Allocate ------------------------------------------------
1099
 
 
1100
 
void * SmallObjAllocator::Allocate( std::size_t numBytes, bool doThrow )
1101
 
{
1102
 
    if ( numBytes > GetMaxObjectSize() )
1103
 
        return DefaultAllocator( numBytes, doThrow );
1104
 
 
1105
 
    assert( NULL != pool_ );
1106
 
    if ( 0 == numBytes ) numBytes = 1;
1107
 
    const std::size_t index = GetOffset( numBytes, GetAlignment() ) - 1;
1108
 
    const std::size_t allocCount = GetOffset( GetMaxObjectSize(), GetAlignment() );
1109
 
    (void) allocCount;
1110
 
    assert( index < allocCount );
1111
 
 
1112
 
    FixedAllocator & allocator = pool_[ index ];
1113
 
    assert( allocator.BlockSize() >= numBytes );
1114
 
    assert( allocator.BlockSize() < numBytes + GetAlignment() );
1115
 
    void * place = allocator.Allocate();
1116
 
 
1117
 
    if ( ( NULL == place ) && TrimExcessMemory() )
1118
 
        place = allocator.Allocate();
1119
 
 
1120
 
    if ( ( NULL == place ) && doThrow )
1121
 
    {
1122
 
#ifdef _MSC_VER
1123
 
        throw std::bad_alloc( "could not allocate small object" );
1124
 
#else
1125
 
        // GCC did not like a literal string passed to std::bad_alloc.
1126
 
        // so just throw the default-constructed exception.
1127
 
        throw std::bad_alloc();
1128
 
#endif
1129
 
    }
1130
 
    return place;
1131
 
}
1132
 
 
1133
 
// SmallObjAllocator::Deallocate ----------------------------------------------
1134
 
 
1135
 
void SmallObjAllocator::Deallocate( void * p, std::size_t numBytes )
1136
 
{
1137
 
    if ( NULL == p ) return;
1138
 
    if ( numBytes > GetMaxObjectSize() )
1139
 
    {
1140
 
        DefaultDeallocator( p );
1141
 
        return;
1142
 
    }
1143
 
    assert( NULL != pool_ );
1144
 
    if ( 0 == numBytes ) numBytes = 1;
1145
 
    const std::size_t index = GetOffset( numBytes, GetAlignment() ) - 1;
1146
 
    const std::size_t allocCount = GetOffset( GetMaxObjectSize(), GetAlignment() );
1147
 
    (void) allocCount;
1148
 
    assert( index < allocCount );
1149
 
    FixedAllocator & allocator = pool_[ index ];
1150
 
    assert( allocator.BlockSize() >= numBytes );
1151
 
    assert( allocator.BlockSize() < numBytes + GetAlignment() );
1152
 
    const bool found = allocator.Deallocate( p, NULL );
1153
 
    (void) found;
1154
 
    assert( found );
1155
 
}
1156
 
 
1157
 
// SmallObjAllocator::Deallocate ----------------------------------------------
1158
 
 
1159
 
void SmallObjAllocator::Deallocate( void * p )
1160
 
{
1161
 
    if ( NULL == p ) return;
1162
 
    assert( NULL != pool_ );
1163
 
    FixedAllocator * pAllocator = NULL;
1164
 
    const std::size_t allocCount = GetOffset( GetMaxObjectSize(), GetAlignment() );
1165
 
    Chunk * chunk = NULL;
1166
 
 
1167
 
    for ( std::size_t ii = 0; ii < allocCount; ++ii )
1168
 
    {
1169
 
        chunk = pool_[ ii ].HasBlock( p );
1170
 
        if ( NULL != chunk )
1171
 
        {
1172
 
            pAllocator = &pool_[ ii ];
1173
 
            break;
1174
 
        }
1175
 
    }
1176
 
    if ( NULL == pAllocator )
1177
 
    {
1178
 
        DefaultDeallocator( p );
1179
 
        return;
1180
 
    }
1181
 
 
1182
 
    assert( NULL != chunk );
1183
 
    const bool found = pAllocator->Deallocate( p, chunk );
1184
 
    (void) found;
1185
 
    assert( found );
1186
 
}
1187
 
 
1188
 
// SmallObjAllocator::IsCorrupt -----------------------------------------------
1189
 
 
1190
 
bool SmallObjAllocator::IsCorrupt( void ) const
1191
 
{
1192
 
    if ( NULL == pool_ )
1193
 
    {
1194
 
        assert( false );
1195
 
        return true;
1196
 
    }
1197
 
    if ( 0 == GetAlignment() )
1198
 
    {
1199
 
        assert( false );
1200
 
        return true;
1201
 
    }
1202
 
    if ( 0 == GetMaxObjectSize() )
1203
 
    {
1204
 
        assert( false );
1205
 
        return true;
1206
 
    }
1207
 
    const std::size_t allocCount = GetOffset( GetMaxObjectSize(), GetAlignment() );
1208
 
    for ( std::size_t ii = 0; ii < allocCount; ++ii )
1209
 
    {
1210
 
        if ( pool_[ ii ].IsCorrupt() )
1211
 
            return true;
1212
 
    }
1213
 
    return false;
1214
 
}
1215
 
 
1216
 
} // end namespace Loki
1217