#ifndef _DMAALLOC_H
#define _DMAALLOC_H

#include "batypes.h"

#if (defined(RWDEBUG) && defined(RWMEMDEBUG))
#if (defined(__GNUC__) && defined (__R5900__))

#include <rtdbmalloc.h>
/*
 * Maps:
 *  malloc >>  debug_malloc
 *  calloc >>  debug_calloc
 * realloc >> debug_realloc
 *    free >>    debug_free
 */

#endif /* (defined(__GNUC__) && defined (__R5900__)) */
#endif /* (defined(RWDEBUG) && defined(RWMEMDEBUG))) */

/* Macro to significantly speed up circularRealloc
 *  from within sweOpenLocalPkt */

#define circularReallocMacro( _ptr, _allocator, _memory, _size )          \
MACRO_START                                                               \
{                                                                         \
    const RwUInt32 alsize =                                               \
        ((_size) + (ALIGNMENT - 1)) & ~(ALIGNMENT - 1);                   \
                                                                          \
    if (alsize <= (_allocator)->blockSize)                                \
    {                                                                     \
        circularAllocatorBlock *const activeBlock =                       \
            (_allocator)->activeAllocBlock;                               \
                                                                          \
        if ((_allocator)->lastBlockAllocated == (_memory))                \
        {                                                                 \
            /* It's possible then, first see if we can extend in place */ \
            /* _allocator->activeAllocBlock                               \
             * points to the containing block */                          \
            const RwUInt32 extraSize =                                    \
                (alsize - (_allocator)->lastBlockSize);                   \
                                                                          \
            if ((activeBlock->waterLevel + extraSize) <                   \
                (_allocator)->blockSize)                                  \
            {                                                             \
                /* No problem, make it so */                              \
                activeBlock->waterLevel += extraSize;                     \
                (_allocator)->lastBlockSize += extraSize;                 \
                (_ptr) = (u_long128 *)(_memory);                          \
            }                                                             \
            else                                                          \
            {                                                             \
                (_ptr) = (u_long128 *)                                    \
                         circularRealloc(_allocator, _memory, _size);     \
            }                                                             \
        }                                                                 \
        else                                                              \
        {                                                                 \
            (_ptr) = (u_long128 *)                                        \
                     circularRealloc(_allocator, _memory, _size);         \
        }                                                                 \
    }                                                                     \
    else                                                                  \
    {                                                                     \
        (_ptr) = (u_long128 *)                                            \
                 circularRealloc(_allocator, _memory, _size);             \
    }                                                                     \
}                                                                         \
MACRO_STOP

/* RWPUBLIC */
/* Allocation flags - when the types change, alignment to a cache line occurs
 * to prevent DMA/DCACHE interaction.
 */
#define calDMA_ALLOC 1
#define calDCACHE_ALLOC 2

/* RWPUBLICEND */

/* Define alignment for the blocks allocated */
#define ALIGNMENT 16

/* RWPUBLIC */

/**
 * $ingroup driversky2
 * $typedef RwSkySplitBits128
 */
typedef union RwSkySplitBits128 RwSkySplitBits128;

/**
 * \ingroup driversky2
 * \union RwSkySplitBits128
 * Union for accessing fields of packed 128 bit quadword
 */
union RwSkySplitBits128
{
    u_long128           field128;     /**< Packed 128 bit quadword  */
    RwUInt64            field_64[2];  /**< Composite bi-word fields */
    RwSplitBits         field_32[4];  /**< Composite quad-word fields */
};

typedef struct circularAllocatorBlock circularAllocatorBlock;
struct circularAllocatorBlock
{
    circularAllocatorBlock *next;                       /**< Internal use */
    RwUInt32 waterLevel;                                /**< Internal use */
    RwUInt32 numBlocksAlloced;                          /**< Internal use */
    void *memory;                                       /**< Internal use */
    void *realMemory;                                   /**< Internal use */
};

typedef struct circularAllocator circularAllocator;
struct circularAllocator
{
    RwUInt32 blockSize;                                 /**< Internal use */

    circularAllocatorBlock *firstBlock;                 /**< Internal use */
    circularAllocatorBlock *activeAllocBlock;           /**< Internal use */
    circularAllocatorBlock *activeFreeBlock;            /**< Internal use */

    void *lastBlockAllocated;                           /**< Internal use */
    RwUInt32 lastBlockSize;                             /**< Internal use */
    RwUInt32 lastBlockFlags;                            /**< Internal use */
    RwUInt32 showNewBlocks;                             /**< Internal use */
    RwUInt32 limit;                                     /**< Internal use */
};

/* Max size of block the circular allocator will allocate */
extern RwUInt32 sweCircAllocBlockSize;

extern void *
circularMalloc(circularAllocator *allocator,
               RwUInt32 size,
               RwUInt32 flags);

extern void *
circularRealloc(circularAllocator *allocator,
                void *memory,
                RwUInt32 size);

extern void 
circularFree(circularAllocator *allocator,
             void *memory);

extern circularAllocator *
circularAllocOpen(RwUInt32 blockSize);

extern RwBool 
circularAllocClose(circularAllocator *allocator);

extern RwBool 
circularAllocPreAlloc(circularAllocator *allocator, 
                      int size);

extern RwBool 
circularLimitAlloc(circularAllocator *allocator, 
                   RwBool limit);
/* RWPUBLICEND */

extern RwUInt32 sweCircAllocBlockSize;

#endif /* _DMAALLOC_H */

