heapsimple.cpp revision 66cd15f94910b1afc64c42375d40e01d5d33ad2f
/* $Id$ */
/** @file
* IPRT - A Simple Heap.
*/
/*
* Copyright (C) 2006-2007 Sun Microsystems, Inc.
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*
* The contents of this file may alternatively be used under the terms
* of the Common Development and Distribution License Version 1.0
* (CDDL) only, as it comes in the "COPYING.CDDL" file of the
* VirtualBox OSE distribution, in which case the provisions of the
* CDDL are applicable instead of those of the GPL.
*
* You may elect to license modified versions of this file under the
* terms and conditions of either the GPL or the CDDL or both.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
* Clara, CA 95054 USA or visit http://www.sun.com if you need
* additional information or have any questions.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#define LOG_GROUP RTLOGGROUP_DEFAULT
#if defined(IN_GUEST_R0) && defined(RT_OS_LINUX)
#include "the-linux-kernel.h"
#endif
/*******************************************************************************
* Structures and Typedefs *
*******************************************************************************/
/** Pointer to the heap anchor block. */
typedef struct RTHEAPSIMPLEINTERNAL *PRTHEAPSIMPLEINTERNAL;
/** Pointer to a heap block. */
typedef struct RTHEAPSIMPLEBLOCK *PRTHEAPSIMPLEBLOCK;
/** Pointer to a free heap block. */
typedef struct RTHEAPSIMPLEFREE *PRTHEAPSIMPLEFREE;
/**
* Structure describing a simple heap block.
* If this block is allocated, it is followed by the user user data.
* If this block is free, see RTHEAPSIMPLEFREE.
*/
typedef struct RTHEAPSIMPLEBLOCK
{
/** The next block in the global block list. */
/** The previous block in the global block list. */
/** Pointer to the heap anchor block. */
/** Flags + magic. */
/** The block is free if this flag is set. When cleared it's allocated. */
/** The magic value. */
/** The mask that needs to be applied to RTHEAPSIMPLEBLOCK::fFalgs to obtain the magic value. */
/**
* Checks if the specified block is valid or not.
* @returns boolean answer.
* @param pBlock Pointer to a RTHEAPSIMPLEBLOCK structure.
*/
#define RTHEAPSIMPLEBLOCK_IS_VALID(pBlock) \
/**
* Checks if the specified block is valid and in use.
* @returns boolean answer.
* @param pBlock Pointer to a RTHEAPSIMPLEBLOCK structure.
*/
#define RTHEAPSIMPLEBLOCK_IS_VALID_USED(pBlock) \
/**
* Checks if the specified block is valid and free.
* @returns boolean answer.
* @param pBlock Pointer to a RTHEAPSIMPLEBLOCK structure.
*/
#define RTHEAPSIMPLEBLOCK_IS_VALID_FREE(pBlock) \
/**
* Checks if the specified block is free or not.
* @returns boolean answer.
* @param pBlock Pointer to a valid RTHEAPSIMPLEBLOCK structure.
*/
/**
* A free heap block.
* This is an extended version of RTHEAPSIMPLEBLOCK that takes the unused
* user data to store free list pointers and a cached size value.
*/
typedef struct RTHEAPSIMPLEFREE
{
/** Core stuff. */
/** Pointer to the next free block. */
/** Pointer to the previous free block. */
/** The size of the block (excluding the RTHEAPSIMPLEBLOCK part). */
/** An alignment filler to make it a multiple of (sizeof(void *) * 2). */
/**
* The heap anchor block.
* This structure is placed at the head of the memory block specified to RTHeapSimpleInit(),
* which means that the first RTHEAPSIMPLEBLOCK appears immediately after this structure.
*/
typedef struct RTHEAPSIMPLEINTERNAL
{
/** The typical magic (RTHEAPSIMPLE_MAGIC). */
/** The heap size. (This structure is not included!) */
/** Pointer to the end of the heap. */
void *pvEnd;
/** The amount of free memory in the heap. */
/** Free head pointer. */
/** Free tail pointer. */
/** Make the size of this structure is a multiple of 32. */
/** The minimum allocation size. */
#define RTHEAPSIMPLE_MIN_BLOCK (sizeof(RTHEAPSIMPLEBLOCK))
/** The minimum and default alignment. */
#define RTHEAPSIMPLE_ALIGNMENT (sizeof(RTHEAPSIMPLEBLOCK))
/*******************************************************************************
* Defined Constants And Macros *
*******************************************************************************/
#ifdef RT_STRICT
# define RTHEAPSIMPLE_STRICT 1
#endif
#define ASSERT_L(a, b) AssertMsg((uintptr_t)(a) < (uintptr_t)(b), ("a=%p b=%p\n", (uintptr_t)(a), (uintptr_t)(b)))
#define ASSERT_LE(a, b) AssertMsg((uintptr_t)(a) <= (uintptr_t)(b), ("a=%p b=%p\n", (uintptr_t)(a), (uintptr_t)(b)))
#define ASSERT_G(a, b) AssertMsg((uintptr_t)(a) > (uintptr_t)(b), ("a=%p b=%p\n", (uintptr_t)(a), (uintptr_t)(b)))
#define ASSERT_GE(a, b) AssertMsg((uintptr_t)(a) >= (uintptr_t)(b), ("a=%p b=%p\n", (uintptr_t)(a), (uintptr_t)(b)))
#define ASSERT_ALIGN(a) AssertMsg(!((uintptr_t)(a) & (RTHEAPSIMPLE_ALIGNMENT - 1)), ("a=%p\n", (uintptr_t)(a)))
{ \
} \
else \
} while (0)
{ \
} \
} while (0)
} while (0)
} while (0)
{ \
} \
else \
} while (0)
{ \
} \
else \
} while (0)
#ifdef RTHEAPSIMPLE_STRICT
do { size_t cbCalc = ((pBlock)->Core.pNext ? (uintptr_t)(pBlock)->Core.pNext : (uintptr_t)(pHeapInt)->pvEnd) \
} while (0)
#else
#endif
/** Asserts that a free block is valid. */
} while (0)
/** Asserts that the heap anchor block is ok. */
#define ASSERT_ANCHOR(pHeapInt) \
} while (0)
/*******************************************************************************
* Internal Functions *
*******************************************************************************/
#ifdef RTHEAPSIMPLE_STRICT
#endif
static PRTHEAPSIMPLEBLOCK rtHeapSimpleAllocBlock(PRTHEAPSIMPLEINTERNAL pHeapInt, size_t cb, size_t uAlignment);
/**
* Initializes the heap.
*
* @returns IPRT status code on success.
* @param pHeap Where to store the heap anchor block on success.
* @param pvMemory Pointer to the heap memory.
* @param cbMemory The size of the heap memory.
*/
{
unsigned i;
/*
* Validate input. The imposed minimum heap size is just a convenien value.
*/
/*
* Place the heap anchor block at the start of the heap memory,
* enforce 32 byte alignment of it. Also align the heap size correctly.
*/
{
}
/* Init the heap anchor block. */
- sizeof(RTHEAPSIMPLEBLOCK)
- sizeof(RTHEAPSIMPLEINTERNAL);
/* Init the single free block. */
#ifdef RTHEAPSIMPLE_STRICT
#endif
return VINF_SUCCESS;
}
/**
* Allocates memory from the specified simple heap.
*
* @returns Pointer to the allocated memory block on success.
* @returns NULL if the request cannot be satisfied. (A VERR_NO_MEMORY condition.)
*
* @param Heap The heap to allocate the memory on.
* @param cb The requested heap block size.
* @param cbAlignment The requested heap block alignment. Pass 0 for default alignment.
* Must be a power of 2.
*/
{
/*
* Validate and adjust the input.
*/
if (cb < RTHEAPSIMPLE_MIN_BLOCK)
else
if (!cbAlignment)
else
{
}
/*
* Do the allocation.
*/
{
return pv;
}
return NULL;
}
/**
* Allocates zeroed memory from the specified simple heap.
*
* @returns Pointer to the allocated memory block on success.
* @returns NULL if the request cannot be satisfied. (A VERR_NO_MEMORY condition.)
*
* @param Heap The heap to allocate the memory on.
* @param cb The requested heap block size.
* @param cbAlignment The requested heap block alignment. Pass 0 for default alignment.
* Must be a power of 2.
*/
{
/*
* Validate and adjust the input.
*/
if (cb < RTHEAPSIMPLE_MIN_BLOCK)
else
if (!cbAlignment)
else
{
}
/*
* Do the allocation.
*/
{
return pv;
}
return NULL;
}
/**
* Allocates a block of memory from the specified heap.
*
* No parameter validation or adjustment is preformed.
*
* @returns Pointer to the allocated block.
* @returns NULL on failure.
* @param pHeapInt The heap.
* @param cb Size of the memory block to allocate.
* @param uAlignment The alignment specifications for the allocated block.
*/
static PRTHEAPSIMPLEBLOCK rtHeapSimpleAllocBlock(PRTHEAPSIMPLEINTERNAL pHeapInt, size_t cb, size_t uAlignment)
{
#ifdef RTHEAPSIMPLE_STRICT
#endif
/*
* Search for a fitting block from the lower end of the heap.
*/
{
/*
* Match for size and alignment.
*/
continue;
if (offAlign)
{
continue;
/*
* Make a stack copy of the free block header and adjust the pointer.
*/
/*
* Donate offAlign bytes to the node in front of us.
* If we're the head node, we'll have to create a fake node. We'll
* mark it USED for simplicity.
*
* (Should this policy of donating memory to the guy in front of us
* cause big 'leaks', we could create a new free node if there is room
* for that.)
*/
if (pPrev)
{
}
else
{
}
/*
* Recreate pFree in the new position and adjust the neighbours.
*/
/* the core */
/* the free part */
else
else
}
/*
* Split off a new FREE block?
*/
{
/*
* Move the FREE block up to make room for the new USED block.
*/
PRTHEAPSIMPLEFREE pNew = (PRTHEAPSIMPLEFREE)((uintptr_t)&pFree->Core + cb + sizeof(RTHEAPSIMPLEBLOCK));
else
else
/*
* Update the old FREE node making it a USED node.
*/
}
else
{
/*
* Link it out of the free list.
*/
else
else
/*
* Convert it to a used block.
*/
}
break;
}
#ifdef RTHEAPSIMPLE_STRICT
#endif
return pRet;
}
/**
* Frees memory allocated from a simple heap.
*
* @param Heap The heap. This is optional and will only be used for strict assertions.
* @param pv The heap block returned by RTHeapSimple
*/
{
/*
* Validate input.
*/
if (!pv)
return;
/*
* Get the block and heap. If in strict mode, validate these.
*/
#ifdef RTHEAPSIMPLE_FREE_POISON
/*
* Poison the block.
*/
#endif
/*
* Call worker which does the actual job.
*/
}
/**
* Free memory a memory block.
*
* @param pHeapInt The heap.
* @param pBlock The memory block to free.
*/
{
#ifdef RTHEAPSIMPLE_STRICT
#endif
/*
* Look for the closest free list blocks by walking the blocks right
* of us (both list are sorted on address).
*/
{
{
}
if (!pRight)
else
{
}
if (pLeft)
}
/*
* Insert at the head of the free block list?
*/
if (!pLeft)
{
if (pRight)
else
}
else
{
/*
* Can we merge with left hand free block?
*/
{
}
/*
* No, just link it into the free list then.
*/
else
{
if (pRight)
else
}
}
/*
* Can we merge with right hand free block?
*/
if ( pRight
{
/* core */
/* free */
else
}
/*
* Calculate the size and update free stats.
*/
#ifdef RTHEAPSIMPLE_STRICT
#endif
}
#ifdef RTHEAPSIMPLE_STRICT
/**
* Internal consitency check (relying on assertions).
* @param pHeapInt
*/
{
{
{
}
else
}
}
#endif
/**
* Gets the size of the specified heap block.
*
* @returns The actual size of the heap block.
* @returns 0 if \a pv is NULL or it doesn't point to a valid heap block. An invalid \a pv
* can also cause traps or trigger assertions.
* @param Heap The heap. This is optional and will only be used for strict assertions.
* @param pv The heap block returned by RTHeapSimple
*/
{
/*
* Validate input.
*/
if (!pv)
return 0;
AssertPtrReturn(pv, 0);
/*
* Get the block and heap. If in strict mode, validate these.
*/
/*
* Calculate the block size.
*/
return cbBlock;
}
/**
* Gets the size of the heap.
*
* This size includes all the internal heap structures. So, even if the heap is
* empty the RTHeapSimpleGetFreeSize() will never reach the heap size returned
* by this function.
*
* @returns The heap size.
* @returns 0 if heap was safely detected as being bad.
* @param Heap The heap.
*/
{
if (Heap == NIL_RTHEAPSIMPLE)
return 0;
AssertPtrReturn(pHeapInt, 0);
}
/**
* Returns the sum of all free heap blocks.
*
* This is the amount of memory you can theoretically allocate
* if you do allocations exactly matching the free blocks.
*
* @returns The size of the free blocks.
* @returns 0 if heap was safely detected as being bad.
* @param Heap The heap.
*/
{
if (Heap == NIL_RTHEAPSIMPLE)
return 0;
AssertPtrReturn(pHeapInt, 0);
}
/**
* Dumps the hypervisor heap.
*
* @param Heap The heap handle.
* @param pfnPrintf Printf like function that groks IPRT formatting.
*/
{
pfnPrintf("**** Dumping Heap %p - cbHeap=%zx cbFree=%zx ****\n",
{
pfnPrintf("%p %06x FREE pNext=%p pPrev=%p fFlags=%#x cb=%#06x : cb=%#06x pNext=%p pPrev=%p\n",
pBlock, (uintptr_t)pBlock - (uintptr_t)(pHeapInt + 1), pBlock->Core.pNext, pBlock->Core.pPrev, pBlock->Core.fFlags, cb,
else
pfnPrintf("%p %06x USED pNext=%p pPrev=%p fFlags=%#x cb=%#06x\n",
pBlock, (uintptr_t)pBlock - (uintptr_t)(pHeapInt + 1), pBlock->Core.pNext, pBlock->Core.pPrev, pBlock->Core.fFlags, cb);
}
}