lvmemman.h 5.47 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206
/** \file lvmemman.h
    \brief Fast memory manager implementation

    CoolReader Engine

    (c) Vadim Lopatin, 2000-2006
    This source code is distributed under the terms of
    GNU General Public License.

    See LICENSE file for details.

*/

#ifndef __LV_MEM_MAN_H_INCLUDED__
#define __LV_MEM_MAN_H_INCLUDED__


#include "crsetup.h"
#include "lvtypes.h"

#define CR_FATAL_ERROR_UNKNOWN             -1
#define CR_FATAL_ERROR_INDEX_OUT_OF_BOUND   1

/// fatal error function type
typedef void (lv_FatalErrorHandler_t)(int errorCode, const char * errorText );

/// fatal error function calls fatal error handler
void crFatalError( int code, const char * errorText );
inline void crFatalError() { crFatalError( -1, "Unknown fatal error" ); }

/// set fatal error handler
void crSetFatalErrorHandler( lv_FatalErrorHandler_t * handler );

/// typed realloc with result check (size is counted in T), fatal error if failed
template <typename T> T * cr_realloc( T * ptr, size_t newSize ) {
    T * newptr = reinterpret_cast<T*>(realloc(ptr, sizeof(T)*newSize));
    if ( newptr )
        return newptr;
    free(ptr); // to bypass cppcheck warning
    crFatalError(-2, "realloc failed");
    return NULL;
}


#if (LDOM_USE_OWN_MEM_MAN==1)
#include <stdlib.h>

#define THROW_MEM_MAN_EXCEPTION crFatalError(-1, "Memory manager fatal error" );

#define BLOCK_SIZE_GRANULARITY 2
#define LOCAL_STORAGE_COUNT    16
#define FIRST_SLICE_SIZE       16
#define MAX_SLICE_COUNT        24
#define MAX_LOCAL_BLOCK_SIZE   ((1<<BLOCK_SIZE_GRANULARITY)*LOCAL_STORAGE_COUNT)

/// memory block
union ldomMemBlock {
//    struct {
        char buf[4];
//    };
//    struct {
        ldomMemBlock * nextfree;
//    };
};

/// memory allocation slice
struct ldomMemSlice {
    ldomMemBlock * pBlocks; // first block
    ldomMemBlock * pEnd;    // first free byte after last block
    ldomMemBlock * pFree;   // first free block
    size_t block_size;      // size of block
    size_t block_count;     // count of blocks
    size_t blocks_used;     // number of used blocks
    //
    inline ldomMemBlock * blockByIndex( size_t index )
    {
        return (ldomMemBlock *) ( (char*)pBlocks + (block_size*index) );
    }
    inline ldomMemBlock * nextBlock( ldomMemBlock * p )
    {
        return (ldomMemBlock *) ( (char*)p + (block_size) );
    }
    inline ldomMemBlock * prevBlock( ldomMemBlock * p )
    {
        return (ldomMemBlock *) ( (char*)p - (block_size) );
    }
    //
    ldomMemSlice( size_t blockSize, size_t blockCount )
    :   block_size(blockSize),
        block_count(blockCount),
        blocks_used(0)
    {

        pBlocks = (ldomMemBlock *) malloc(block_size * block_count);
        pEnd = blockByIndex(block_count);
        pFree = pBlocks;
        for (ldomMemBlock * p = pBlocks; p<pEnd; )
        {
            p = p->nextfree = nextBlock(p);
        }
        prevBlock(pEnd)->nextfree = NULL;
    }
    ~ldomMemSlice()
    {
        free( pBlocks );
    }
    inline ldomMemBlock * alloc_block()
    {
        ldomMemBlock * res = pFree;
        pFree = res->nextfree;
        ++blocks_used;
        return res;
    }
    inline bool free_block( ldomMemBlock * pBlock )
    {
        if (pBlock < pBlocks || pBlock >= pEnd)
            return false; // chunk does not belong to this slice
        pBlock->nextfree = pFree;
        pFree = pBlock;
        --blocks_used;
        return true;
    }
};

/// storage for blocks of specified size
struct ldomMemManStorage
{
    size_t block_size;      // size of block
    size_t slice_count;     // count of existing blocks
    ldomMemSlice * slices[MAX_SLICE_COUNT];
    //======================================
    ldomMemManStorage( size_t blockSize )
        : block_size(blockSize)
    {
        slice_count = 1;
        slices[0] = new ldomMemSlice(block_size, FIRST_SLICE_SIZE);
    }
    ~ldomMemManStorage()
    {
        for (size_t i=0; i<slice_count; i++)
            delete slices[i];
    }
    void * alloc()
    {
        // search for existing slice
        for (int i=slice_count-1; i>=0; --i)
        {
            if (slices[i]->pFree != NULL)
                return slices[i]->alloc_block();
        }
        // alloc new slice
        if (slice_count >= MAX_SLICE_COUNT)
            THROW_MEM_MAN_EXCEPTION;
        slices[slice_count] = 
            new ldomMemSlice(block_size, FIRST_SLICE_SIZE << (slice_count+1));
        return slices[slice_count++]->alloc_block();
    }
    void free( ldomMemBlock * pBlock )
    {
        for (int i=slice_count-1; i>=0; --i)
        {
            if (slices[i]->free_block(pBlock))
                return;
        }
        //throw; // wrong pointer!!!
    }
};

/// allocate memory
void * ldomAlloc( size_t n );
/// free memory
void   ldomFree( void * p, size_t n );


//////////////////////////////////////////////////////////////////////

/// declare allocator for class: use in class declarations
#define DECLARE_CLASS_ALLOCATOR(classname) \
    void * operator new( size_t size ); \
    void operator delete( void * p );


/// define allocator for class: use in class definitions
#define DEFINE_CLASS_ALLOCATOR(classname) \
\
static ldomMemManStorage * pms ## classname = NULL; \
\
void * classname::operator new( size_t size ) \
{ \
    if (pms ## classname == NULL) \
    { \
        pms ## classname = new ldomMemManStorage(sizeof(classname)); \
    } \
    return pms ## classname->alloc(); \
} \
\
void classname::operator delete( void * p ) \
{ \
    pms ## classname->free((ldomMemBlock *)p); \
}

void ldomFreeStorage();

#endif

#endif //__LV_MEM_MAN_H_INCLUDED__