490 lines
12 KiB
C
490 lines
12 KiB
C
/*++
|
||
|
||
Copyright (c) 1996 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
poolmem.c
|
||
|
||
Abstract:
|
||
|
||
poolmem provides a managed allocation scheme in which large blocks of memory are
|
||
allocated (pools) and then divided up by request into low overhead memory chunks
|
||
upon request. poolmem provides for easy creation/clean-up of memory, freeing the
|
||
developer for more important tasks.
|
||
|
||
Author:
|
||
|
||
Marc R. Whitten (marcw) 13-Feb-1997
|
||
|
||
Revision History:
|
||
|
||
jimschm 28-Sep-1998 Debug message fixes
|
||
|
||
--*/
|
||
|
||
#include "pch.h"
|
||
|
||
typedef struct _POOLMEMORYBLOCK POOLMEMORYBLOCK, *PPOOLMEMORYBLOCK;
|
||
|
||
struct _POOLMEMORYBLOCK {
|
||
DWORD Index; // Tracks into RawMemory.
|
||
DWORD Size; // the size in bytes of RawMemory.
|
||
PPOOLMEMORYBLOCK NextBlock; // A pointer to the next block in the pool chain.
|
||
PPOOLMEMORYBLOCK PrevBlock; // A pointer to the prev block in the pool chain.
|
||
DWORD UseCount; // The number of allocations currently referring
|
||
// to this block.
|
||
PBYTE RawMemory; // The actual bytes of allocable memory in this block.
|
||
};
|
||
|
||
|
||
typedef struct _ALLOCATION ALLOCATION, * PALLOCATION;
|
||
struct _ALLOCATION {
|
||
|
||
PPOOLMEMORYBLOCK ParentBlock; // A reference to the block from which this allocation
|
||
// was created.
|
||
|
||
};
|
||
|
||
typedef struct _POOLHEADER {
|
||
PPOOLMEMORYBLOCK PoolHead; // The active memory block in this pool.
|
||
DWORD MinimumBlockSize; // minimum size to allocate when a new block is needed.
|
||
} POOLHEADER, *PPOOLHEADER;
|
||
|
||
|
||
BOOL
|
||
pPoolMemAddMemory (
|
||
IN POOLHANDLE Handle,
|
||
IN DWORD Size
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
pPoolMemAddMemory is the function responsible for actually growing the size of
|
||
the pool by adding a new block of memory. This function is used by
|
||
PoolMemInitPool and PoolMemGetMemory.
|
||
|
||
when called, this function attempts to allocate at least poolHeader ->
|
||
MinimumBlockSize bytes of memory. If the requested size is actually larger
|
||
than the minimum, the requested size is allocated instead. This is consistent
|
||
with PoolMem's main purpose: An efficient allocator for larger numbers of small
|
||
objects. If PoolMem is being used to allocate very large objects, the benefits
|
||
are lost and poolmem becomes a very inefficient allocator.
|
||
|
||
Arguments:
|
||
|
||
Handle - A Handle to a Pool of Memory.
|
||
|
||
Size - Size to allocate.
|
||
|
||
|
||
Return Value:
|
||
|
||
returns TRUE if memory was successfully added, FALSE otherwise.
|
||
|
||
--*/
|
||
{
|
||
PBYTE allocedMemory;
|
||
PPOOLMEMORYBLOCK newBlock;
|
||
PPOOLHEADER poolHeader = (PPOOLHEADER) Handle;
|
||
DWORD sizeNeeded;
|
||
|
||
//
|
||
// Determine size needed and attempt to allocate memory.
|
||
//
|
||
if (Size + sizeof(POOLMEMORYBLOCK) > poolHeader -> MinimumBlockSize) {
|
||
sizeNeeded = Size + sizeof(POOLMEMORYBLOCK);
|
||
}
|
||
else {
|
||
sizeNeeded = poolHeader -> MinimumBlockSize;
|
||
}
|
||
allocedMemory = MemAlloc(sizeNeeded);
|
||
|
||
if (allocedMemory) {
|
||
|
||
//
|
||
// Use the beginning of the alloc'ed block as the poolblock structure.
|
||
//
|
||
newBlock = (PPOOLMEMORYBLOCK) allocedMemory;
|
||
newBlock -> Size = sizeNeeded - sizeof(POOLMEMORYBLOCK);
|
||
newBlock -> RawMemory = allocedMemory + sizeof(POOLMEMORYBLOCK);
|
||
newBlock -> Index = 0;
|
||
newBlock -> UseCount = 0;
|
||
|
||
//
|
||
// Link the block into the list.
|
||
//
|
||
if (poolHeader -> PoolHead) {
|
||
poolHeader -> PoolHead -> PrevBlock = newBlock;
|
||
}
|
||
newBlock -> NextBlock = poolHeader -> PoolHead;
|
||
newBlock -> PrevBlock = NULL;
|
||
poolHeader -> PoolHead = newBlock;
|
||
|
||
}
|
||
//
|
||
// Assuming allocedMemory is non-NULL, we have succeeded.
|
||
//
|
||
return allocedMemory != NULL;
|
||
|
||
}
|
||
|
||
|
||
POOLHANDLE
|
||
PoolMemInitPool (
|
||
VOID
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Initializes a new memory pool and returns a handle to it.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
If the function completes succssessfully, it returns a valid POOLHANDLE, otherwise,
|
||
it returns NULL.
|
||
|
||
--*/
|
||
|
||
{
|
||
BOOL ableToAddMemory;
|
||
PPOOLHEADER header = NULL;
|
||
|
||
//
|
||
// Allocate the header of this pool.
|
||
//
|
||
header = MemAlloc(sizeof(POOLHEADER));
|
||
|
||
if (header) {
|
||
//
|
||
// Allocation was successful. Now, initialize the pool.
|
||
//
|
||
header -> MinimumBlockSize = POOLMEMORYBLOCKSIZE;
|
||
header -> PoolHead = NULL;
|
||
|
||
//
|
||
// Actually add some memory to the pool.
|
||
//
|
||
ableToAddMemory = pPoolMemAddMemory(header,0);
|
||
|
||
if (!ableToAddMemory) {
|
||
//
|
||
// Unable to add memory to the pool.
|
||
//
|
||
MemFree(header);
|
||
header = NULL;
|
||
}
|
||
}
|
||
|
||
return (POOLHANDLE) header;
|
||
|
||
}
|
||
|
||
VOID
|
||
PoolMemEmptyPool (
|
||
IN POOLHANDLE Handle
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
PoolMemEmptyPool resets the index pointer of the index block back
|
||
to zero, so the next allocation will come from the already allocated
|
||
active block.
|
||
|
||
Calling this function invalidates all pointers previously allocated from
|
||
the active block.
|
||
|
||
Arguments:
|
||
|
||
Handle - Specifies the pool to reset
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PPOOLHEADER poolHeader = (PPOOLHEADER) Handle;
|
||
|
||
poolHeader -> PoolHead -> UseCount = 0;
|
||
poolHeader -> PoolHead -> Index = 0;
|
||
}
|
||
|
||
|
||
|
||
VOID
|
||
PoolMemSetMinimumGrowthSize (
|
||
IN POOLHANDLE Handle,
|
||
IN DWORD Size
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Sets the minimum growth size for a memory pool. This value is used when new blocks
|
||
are actually added to the pool. The PoolMem allocator will attempt to allocate at
|
||
least this minimum size.
|
||
|
||
Arguments:
|
||
|
||
Handle - A valid POOLHANDLE.
|
||
Size - The minimum size in bytes to grow the pool by on each allocation.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PPOOLHEADER poolHeader = (PPOOLHEADER) Handle;
|
||
|
||
poolHeader -> MinimumBlockSize = max(Size,0);
|
||
}
|
||
|
||
|
||
VOID
|
||
PoolMemDestroyPool (
|
||
POOLHANDLE Handle
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
PoolMemDestroyPool completely cleans up the memory pool identified by Handle. It
|
||
simply walks the list of memory blocks associated with the memory pool, freeing each of them.
|
||
|
||
Arguments:
|
||
|
||
Handle - A valid POOLHANDLE.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
PPOOLMEMORYBLOCK nextBlock;
|
||
PPOOLMEMORYBLOCK blockToFree;
|
||
PPOOLHEADER poolHeader;
|
||
|
||
|
||
poolHeader = (PPOOLHEADER) Handle;
|
||
|
||
//
|
||
// Walk the list, freeing as we go.
|
||
//
|
||
blockToFree = poolHeader -> PoolHead;
|
||
|
||
while (blockToFree != NULL) {
|
||
|
||
nextBlock = blockToFree->NextBlock;
|
||
MemFree(blockToFree);
|
||
blockToFree = nextBlock;
|
||
}
|
||
|
||
//
|
||
// Also, deallocate the poolheader itself.
|
||
//
|
||
MemFree(poolHeader);
|
||
|
||
}
|
||
|
||
PVOID
|
||
PoolMemRealGetMemory (
|
||
IN POOLHANDLE Handle,
|
||
IN DWORD Size,
|
||
IN DWORD AlignSize
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
PoolMemRealGetMemory is the worker routine that processes all requests to retrieve memory
|
||
from a pool. Other calls eventually decay into a call to this common routine. This routine
|
||
attempts to service the request out of the current memory block, or, if it cannot, out of
|
||
a newly allocated block.
|
||
|
||
Arguments:
|
||
|
||
(File) - The File from whence the call orignated. This is used for memory tracking and checking
|
||
in the debug version.
|
||
(Line) - The Line from whence the call orignated.
|
||
|
||
Handle - A valid POOLHANDLE.
|
||
Size - Contains the size in bytes that the caller needs from the pool.
|
||
AlignSize - Provides an alignment value. The returned memory will be aligned on <alignsize> byte
|
||
boundaries.
|
||
|
||
Return Value:
|
||
|
||
The allocated memory, or, NULL if no memory could be allocated.
|
||
|
||
--*/
|
||
{
|
||
BOOL haveEnoughMemory = TRUE;
|
||
PVOID rMemory = NULL;
|
||
PPOOLHEADER poolHeader = (PPOOLHEADER) Handle;
|
||
PPOOLMEMORYBLOCK currentBlock;
|
||
PALLOCATION allocation;
|
||
DWORD sizeNeeded;
|
||
DWORD_PTR padLength;
|
||
|
||
//
|
||
// Assume that the current block of memory will be sufficient.
|
||
//
|
||
currentBlock = poolHeader -> PoolHead;
|
||
|
||
//
|
||
// Determine if more memory is needed, attempt to add if needed. Note that the size
|
||
// must include the size of an ALLOCATION struct in addition to the size required
|
||
// by the callee. Note the references to AlignSize in the test below. This is to ensure
|
||
// that there is enough memory to allocate after taking into acount data alignment.
|
||
//
|
||
sizeNeeded = Size + sizeof(ALLOCATION);
|
||
|
||
if (currentBlock -> Size - currentBlock -> Index < sizeNeeded + AlignSize) {
|
||
|
||
haveEnoughMemory = pPoolMemAddMemory(poolHeader,sizeNeeded + AlignSize);
|
||
|
||
//
|
||
// Make sure that the currentBlock is correctly set
|
||
//
|
||
currentBlock = poolHeader -> PoolHead;
|
||
}
|
||
|
||
//
|
||
// If there is enough memory available, return it.
|
||
//
|
||
if (haveEnoughMemory) {
|
||
if (AlignSize) {
|
||
|
||
padLength = (DWORD_PTR) currentBlock + sizeof(POOLMEMORYBLOCK)
|
||
+ currentBlock -> Index + sizeof(ALLOCATION);
|
||
currentBlock -> Index += (DWORD)(AlignSize - (padLength % AlignSize)) % AlignSize;
|
||
|
||
}
|
||
|
||
//
|
||
// Save a reference to this block in the memorys ALLOCATION structure.
|
||
// This will be used to decrease the use count on a block when releasing
|
||
// memory.
|
||
//
|
||
(PBYTE) allocation = &(currentBlock -> RawMemory[currentBlock -> Index]);
|
||
allocation -> ParentBlock = currentBlock;
|
||
|
||
|
||
//
|
||
// Ok, get a reference to the actual memory to return to the user.
|
||
//
|
||
rMemory = (PVOID)
|
||
&(currentBlock->RawMemory[currentBlock -> Index + sizeof(ALLOCATION)]);
|
||
|
||
//
|
||
// Update memory block data fields.
|
||
//
|
||
currentBlock->Index += sizeNeeded;
|
||
currentBlock->UseCount++;
|
||
}
|
||
|
||
return rMemory;
|
||
}
|
||
|
||
VOID
|
||
PoolMemReleaseMemory (
|
||
IN POOLHANDLE Handle,
|
||
IN LPVOID Memory
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
PoolMemReleaseMemory notifies the Pool that a piece of memory is no longer needed.
|
||
if all memory within a non-active block (i.e. not the first block) is released,
|
||
that block will be freed. If all memory is released within an active block, that blocks
|
||
stats are simply cleared, effectively reclaiming its space.
|
||
|
||
Arguments:
|
||
|
||
Handle - A Handle to a Pool of Memory.
|
||
Memory - Contains the address of the memory that is no longer needed.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
PALLOCATION allocation;
|
||
PPOOLHEADER poolHeader = (PPOOLHEADER) Handle;
|
||
|
||
//
|
||
// Get a reference to the ALLOCATION struct that precedes the actual memory.
|
||
//
|
||
allocation = (PALLOCATION) Memory - 1;
|
||
|
||
//
|
||
// Check to make sure this memory has not previously been freed.
|
||
//
|
||
if (allocation -> ParentBlock == NULL) {
|
||
return;
|
||
}
|
||
|
||
//
|
||
// Update the use count on this allocations parent block.
|
||
//
|
||
allocation -> ParentBlock -> UseCount--;
|
||
|
||
|
||
|
||
|
||
if (allocation -> ParentBlock -> UseCount == 0) {
|
||
|
||
//
|
||
// This was the last allocation still referring to the parent block.
|
||
//
|
||
|
||
if (allocation -> ParentBlock != poolHeader -> PoolHead) {
|
||
//
|
||
// Since the parent block isn't the active block, simply delete it.
|
||
//
|
||
|
||
if (allocation -> ParentBlock -> NextBlock) {
|
||
allocation -> ParentBlock -> NextBlock -> PrevBlock =
|
||
allocation -> ParentBlock -> PrevBlock;
|
||
}
|
||
allocation -> ParentBlock -> PrevBlock -> NextBlock =
|
||
allocation -> ParentBlock -> NextBlock;
|
||
MemFree(allocation -> ParentBlock);
|
||
|
||
|
||
}
|
||
else {
|
||
//
|
||
// Since this is the active block, reset it.
|
||
//
|
||
allocation -> ParentBlock -> Index = 0;
|
||
allocation -> ParentBlock = NULL;
|
||
|
||
}
|
||
}
|
||
else {
|
||
allocation -> ParentBlock = NULL;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
|
||
|