932 lines
23 KiB
C
932 lines
23 KiB
C
|
/*++
|
||
|
|
||
|
Copyright (c) 1992 Microsoft Corporation
|
||
|
|
||
|
Module Name:
|
||
|
|
||
|
suballoc.c
|
||
|
|
||
|
Abstract:
|
||
|
|
||
|
This module contains code for managing a paritially commited address
|
||
|
space. It handles allocation of chunks of memory smaller than the
|
||
|
commit granularity. It commits and decommits memory as needed using
|
||
|
the supplied function for committing and decommitting memory. The
|
||
|
structures used for tracking the address space are allocated outside
|
||
|
of the specified addresss space.
|
||
|
|
||
|
Author:
|
||
|
|
||
|
Dave Hastings (daveh) creation-date 21-Jan-1994
|
||
|
|
||
|
Notes:
|
||
|
|
||
|
Since this package does not actually access memory in the address space
|
||
|
it is managing, it will work as well with real linear addresses or
|
||
|
"Intel Addresses" such as would be encountered with the Insignia Emulator
|
||
|
on risc.
|
||
|
|
||
|
Revision History:
|
||
|
|
||
|
|
||
|
--*/
|
||
|
#include "suballcp.h"
|
||
|
|
||
|
PVOID
|
||
|
SAInitialize(
|
||
|
ULONG BaseAddress,
|
||
|
ULONG Size,
|
||
|
PSACOMMITROUTINE CommitRoutine,
|
||
|
PSACOMMITROUTINE DecommitRoutine,
|
||
|
PSAMEMORYMOVEROUTINE MemoryMoveRoutine
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This function performs initialization of the sub allocation package
|
||
|
for the specified addresss range. It allocates the data structures
|
||
|
necessary to track the allocations
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
BaseAddress -- Supplies the base address of the address space to
|
||
|
sub allocate.
|
||
|
Size -- Supplies the size in bytes of the address space to sub allocate.
|
||
|
CommitRoutine -- Supplies a pointer to the routine used to commit regions
|
||
|
of the address space.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
If the function was successful it returns a pointer to the sub-allocation
|
||
|
data structures. Otherwise it returns NULL.
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
PSUBALLOCATIONDATA SubAlloc;
|
||
|
ULONG SASize;
|
||
|
|
||
|
ASSERT_STEPTHROUGH;
|
||
|
//
|
||
|
// Asserts to insure that everything is as we expect it to be
|
||
|
//
|
||
|
ASSERT(((COMMIT_GRANULARITY % SUBALLOC_GRANULARITY) == 0));
|
||
|
|
||
|
//
|
||
|
// Allocate the tracking structure
|
||
|
//
|
||
|
// SUBALLOCATIONDATA is declared with 1 uchar for the bitmap.
|
||
|
// this is the reason for subtracting one from the total size
|
||
|
// calculation.
|
||
|
//
|
||
|
SASize = sizeof(SUBALLOCATIONDATA)
|
||
|
+ (Size / SUBALLOC_GRANULARITY) / sizeof(UCHAR) - 1;
|
||
|
|
||
|
SubAlloc = malloc(SASize);
|
||
|
|
||
|
if (SubAlloc == NULL) {
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Initialize the structure
|
||
|
//
|
||
|
RtlZeroMemory(SubAlloc, SASize);
|
||
|
|
||
|
INIT_SUBALLOC_SIGNATURE(SubAlloc);
|
||
|
SubAlloc->BaseAddress = BaseAddress;
|
||
|
SubAlloc->Size = Size / SUBALLOC_GRANULARITY;
|
||
|
SubAlloc->CommitRoutine = CommitRoutine;
|
||
|
SubAlloc->DecommitRoutine = DecommitRoutine;
|
||
|
SubAlloc->MoveMemRoutine = MemoryMoveRoutine;
|
||
|
|
||
|
return SubAlloc;
|
||
|
}
|
||
|
|
||
|
BOOL
|
||
|
SAQueryFree(
|
||
|
PVOID SubAllocation,
|
||
|
PULONG FreeBytes,
|
||
|
PULONG LargestFreeBlock
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This routine returns the number of free bytes in the
|
||
|
sub allocated address space.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
SubAllocation -- Supplies the pointer returned by SAInitialize
|
||
|
FreeBytes -- Returns the number of free bytes
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
TRUE -- if successful, and FreeBytes contains the number of free bytes.
|
||
|
FALSE otherwise
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
ULONG i, FreeCount;
|
||
|
PSUBALLOCATIONDATA SubAlloc;
|
||
|
ULONG TempLargest, LargestBlock;
|
||
|
|
||
|
ASSERT_STEPTHROUGH;
|
||
|
|
||
|
//
|
||
|
// Get a typed pointer
|
||
|
//
|
||
|
SubAlloc = SubAllocation;
|
||
|
|
||
|
//
|
||
|
// Make sure that we have what we think we do
|
||
|
//
|
||
|
ASSERT_SUBALLOC(SubAlloc);
|
||
|
|
||
|
//
|
||
|
// Count the free chunks and find largest block
|
||
|
//
|
||
|
FreeCount = 0;
|
||
|
LargestBlock = 0;
|
||
|
i = 0;
|
||
|
while (i < SubAlloc->Size) {
|
||
|
|
||
|
TempLargest = 0;
|
||
|
while (
|
||
|
(i < SubAlloc->Size) &&
|
||
|
(GET_BIT_FROM_CHAR_ARRAY(SubAlloc->Allocated, i) == 0)
|
||
|
){
|
||
|
FreeCount++;
|
||
|
TempLargest++;
|
||
|
i++;
|
||
|
}
|
||
|
|
||
|
if (TempLargest > LargestBlock) {
|
||
|
LargestBlock = TempLargest;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Skip allocated blocks
|
||
|
//
|
||
|
while (
|
||
|
(i < SubAlloc->Size) &&
|
||
|
(GET_BIT_FROM_CHAR_ARRAY(SubAlloc->Allocated, i) == 1)
|
||
|
) {
|
||
|
i++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
*FreeBytes = FreeCount * SUBALLOC_GRANULARITY;
|
||
|
*LargestFreeBlock = LargestBlock * SUBALLOC_GRANULARITY;
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
BOOL
|
||
|
SAAllocate(
|
||
|
PVOID SubAllocation,
|
||
|
ULONG Size,
|
||
|
PULONG Address
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This function allocates a portion of the address space described by
|
||
|
SubAllocation. If necessary, it will commit additional blocks.
|
||
|
Size is rounded up to the next higher multiple of SUBALLOC_GRANULARITY.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
SubAllocation -- Supplies the pointer returned by SAInitialize.
|
||
|
Size -- Supplies the size in bytes of the region to allocate.
|
||
|
Address -- Returns the address of the region allocated.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
TRUE if successful. If false is returned, no address is returned.
|
||
|
|
||
|
Notes:
|
||
|
|
||
|
Zero is a valid value for the returned address.
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
ULONG AllocateSize, i, CurrentChunk;
|
||
|
BOOL Done = FALSE;
|
||
|
PSUBALLOCATIONDATA SubAlloc;
|
||
|
BOOL Success;
|
||
|
|
||
|
ASSERT_STEPTHROUGH;
|
||
|
|
||
|
//
|
||
|
// Get a typed pointer. This allows us to avoid
|
||
|
// casting every time we access the pointer.
|
||
|
//
|
||
|
SubAlloc = SubAllocation;
|
||
|
|
||
|
ASSERT_SUBALLOC(SubAlloc);
|
||
|
|
||
|
//
|
||
|
// Round size and make into number of blocks
|
||
|
//
|
||
|
AllocateSize = ALLOC_ROUND(Size);
|
||
|
|
||
|
//
|
||
|
// Find a chunk that is free
|
||
|
//
|
||
|
// We need this loop in spite of the fact that we
|
||
|
// are keeping an index to the first free block.
|
||
|
// We update this pointer somewhat heuristically.
|
||
|
// If we allocate the first free block, we update
|
||
|
// the index to point past the block we allocated.
|
||
|
// We don't repeat the free scan however, so the
|
||
|
// index may actually point to an allocated block.
|
||
|
//
|
||
|
CurrentChunk = SubAlloc->FirstFree;
|
||
|
while (CurrentChunk < SubAlloc->Size) {
|
||
|
if (GET_BIT_FROM_CHAR_ARRAY(SubAlloc->Allocated, CurrentChunk) == 0) {
|
||
|
SubAlloc->FirstFree = CurrentChunk;
|
||
|
break;
|
||
|
}
|
||
|
CurrentChunk++;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Find a block that is big enough
|
||
|
//
|
||
|
while (!Done && (CurrentChunk < SubAlloc->Size)){
|
||
|
|
||
|
//
|
||
|
// Search for a contiguous block large enough
|
||
|
//
|
||
|
for (i = 0; i < AllocateSize; i++){
|
||
|
//
|
||
|
// Insure we don't walk off the end of the data structure
|
||
|
//
|
||
|
if ((i + CurrentChunk) >= SubAlloc->Size){
|
||
|
CurrentChunk += i; // Satisfy termination condition
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Check to see if this chunk is free
|
||
|
//
|
||
|
if (
|
||
|
GET_BIT_FROM_CHAR_ARRAY(
|
||
|
SubAlloc->Allocated,
|
||
|
i + CurrentChunk
|
||
|
)
|
||
|
== 0
|
||
|
){
|
||
|
continue;
|
||
|
} else {
|
||
|
//
|
||
|
// Chunk is not free, so advance the search
|
||
|
//
|
||
|
CurrentChunk += i + 1;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Check to see if we found a chunk
|
||
|
//
|
||
|
if (i == AllocateSize) {
|
||
|
Done = TRUE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// If we found the chunk, commit it (if necessary) and mark it allocated
|
||
|
//
|
||
|
// N.B. It is important to commit it first, and mark it allocated last,
|
||
|
// because we use the allocated bits to determine if the chunk is
|
||
|
// committed. If all of the allocated bits are clear, the chunk
|
||
|
// is not commited yet.
|
||
|
//
|
||
|
if (Done) {
|
||
|
|
||
|
//
|
||
|
// Allocate and commit the memory
|
||
|
//
|
||
|
Success = AllocateChunkAt(
|
||
|
SubAlloc,
|
||
|
AllocateSize,
|
||
|
CurrentChunk,
|
||
|
FALSE
|
||
|
);
|
||
|
|
||
|
if (!Success) {
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
*Address = BLOCK_INDEX_TO_ADDRESS(SubAlloc, CurrentChunk);
|
||
|
|
||
|
ASSERT((SubAlloc->BaseAddress <= *Address) &&
|
||
|
((SubAlloc->BaseAddress + SubAlloc->Size * SUBALLOC_GRANULARITY)
|
||
|
> *Address));
|
||
|
#if 0
|
||
|
{
|
||
|
char Buffer[80];
|
||
|
|
||
|
sprintf(Buffer, "SAAllocate: Allocating at address %lx\n", *Address);
|
||
|
OutputDebugString(Buffer);
|
||
|
}
|
||
|
#endif
|
||
|
return TRUE;
|
||
|
|
||
|
} else {
|
||
|
return FALSE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
BOOL
|
||
|
SAFree(
|
||
|
PVOID SubAllocation,
|
||
|
ULONG Size,
|
||
|
ULONG Address
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This routine frees a sub-allocated chunk of memory. If the
|
||
|
entire commited block (or blocks) that the specified chunk
|
||
|
belongs to are free, the chunks are decommitted. Address is
|
||
|
rounded down to the next lower SUBALLOC_GRANULARITY boundary.
|
||
|
size is rounded up to the next higher multiple of SUBALLOC_GRANULARITY.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
SubAllocation -- Supplies the pointer returned by SAInitialize.
|
||
|
Size -- Supplies the size in bytes of the region to free.
|
||
|
Address -- Supplies the address of the region to free.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
TRUE if successful.
|
||
|
|
||
|
Notes:
|
||
|
|
||
|
It is possible to free a different size at a particular
|
||
|
address than was allocated. This will not cause the
|
||
|
SubAllocation package any problems.
|
||
|
|
||
|
BUGBUG decommit error handling?
|
||
|
--*/
|
||
|
{
|
||
|
PSUBALLOCATIONDATA SubAlloc;
|
||
|
ULONG AllocatedSize, BaseBlock;
|
||
|
|
||
|
SubAlloc = SubAllocation;
|
||
|
ASSERT_SUBALLOC(SubAlloc);
|
||
|
|
||
|
//
|
||
|
// Make sure that the space to free is really ours
|
||
|
// (Entire block within range, and correctly aligned)
|
||
|
if (
|
||
|
(Address < SubAlloc->BaseAddress) ||
|
||
|
(Address >= (SubAlloc->BaseAddress + SubAlloc->Size * SUBALLOC_GRANULARITY)) ||
|
||
|
((Address + Size) > (SubAlloc->BaseAddress + SubAlloc->Size * SUBALLOC_GRANULARITY)) ||
|
||
|
(Address % SUBALLOC_GRANULARITY)
|
||
|
) {
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Turn Address into Block #
|
||
|
//
|
||
|
BaseBlock = ADDRESS_TO_BLOCK_INDEX(SubAlloc, Address);
|
||
|
|
||
|
//
|
||
|
// Round up the size
|
||
|
//
|
||
|
AllocatedSize = ALLOC_ROUND(Size);
|
||
|
|
||
|
return FreeChunk(
|
||
|
SubAlloc,
|
||
|
AllocatedSize,
|
||
|
BaseBlock
|
||
|
);
|
||
|
}
|
||
|
|
||
|
BOOL
|
||
|
SAReallocate(
|
||
|
PVOID SubAllocation,
|
||
|
ULONG OriginalSize,
|
||
|
ULONG OriginalAddress,
|
||
|
ULONG NewSize,
|
||
|
PULONG NewAddress
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This routine reallocates a sub allocated block of memory.
|
||
|
The sizes are rounded up to the next SUBALLOC_GRANULARITY.
|
||
|
The Original address is rounded down to the next SUBALLOC_GRANULARITY
|
||
|
boundary. Only min(OriginalSize, NewSize) bytes of data are copied to
|
||
|
the new block. The block changed in place if possible.
|
||
|
|
||
|
The following is an enumation of the possible successful reallocs.
|
||
|
|
||
|
1. NewSize < OriginalSize
|
||
|
free block tail
|
||
|
2. NewSize > OriginalSize
|
||
|
a.) Sufficient freespace at OriginalAddress + OriginalSize
|
||
|
Allocate the space at the tail of the block
|
||
|
b.) Sufficient free space at OriginalAddress - size delta
|
||
|
Allocate the space at the beginning of the block, and
|
||
|
copy the data.
|
||
|
c.) Sufficient space elsewhere in the address space
|
||
|
Allocate the space, and copy the block.
|
||
|
|
||
|
If none of the above is true, the realloc fails. The above are
|
||
|
in order of preference.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
SubAllocation -- Supplies the pointer returned by SAInitialize.
|
||
|
OriginalSize -- Supplies the old size in bytes of the block.
|
||
|
OriginalAddress -- Supplies the old address of the block.
|
||
|
NewSize -- Supplies the new size in bytes of the block.
|
||
|
NewAddress -- Returns the new address of the block.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
True if successful. If unsucessful, no allocation is changed.
|
||
|
|
||
|
Notes:
|
||
|
|
||
|
If the caller does not supply the correct original size for the block,
|
||
|
some memory may be lost, and the block may be moved unnecessarily.
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
|
||
|
ULONG OriginalSizeBlock, NewSizeBlock, OriginalIndex;
|
||
|
ULONG AdditionalBlocks, Address;
|
||
|
BOOL Success;
|
||
|
PSUBALLOCATIONDATA SubAlloc;
|
||
|
|
||
|
SubAlloc = SubAllocation;
|
||
|
ASSERT_SUBALLOC(SubAlloc);
|
||
|
|
||
|
//
|
||
|
// Convert Sizes and address to blocks
|
||
|
//
|
||
|
OriginalSizeBlock = ALLOC_ROUND(OriginalSize);
|
||
|
NewSizeBlock = ALLOC_ROUND(NewSize);
|
||
|
OriginalIndex = ADDRESS_TO_BLOCK_INDEX(SubAlloc, OriginalAddress);
|
||
|
|
||
|
//
|
||
|
// Check to see if we are changing the size of the block
|
||
|
//
|
||
|
// N.B. Because we have rounded the numbers to an allocation
|
||
|
// boundary, the following test may succeed (correctly)
|
||
|
// even though OriginalSize != NewSize
|
||
|
//
|
||
|
if (OriginalSizeBlock == NewSizeBlock) {
|
||
|
*NewAddress = OriginalAddress;
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Check to see if the block is getting smaller
|
||
|
//
|
||
|
if (OriginalSizeBlock > NewSizeBlock) {
|
||
|
|
||
|
//
|
||
|
// Free the tail of the block
|
||
|
//
|
||
|
Success = FreeChunk(
|
||
|
SubAlloc,
|
||
|
OriginalSizeBlock - NewSizeBlock,
|
||
|
OriginalIndex + NewSizeBlock
|
||
|
);
|
||
|
|
||
|
if (Success) {
|
||
|
*NewAddress = OriginalAddress;
|
||
|
return TRUE;
|
||
|
} else {
|
||
|
return FALSE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Try to allocate the space at the end of the block
|
||
|
//
|
||
|
AdditionalBlocks = NewSizeBlock - OriginalSizeBlock;
|
||
|
|
||
|
Success = AllocateChunkAt(
|
||
|
SubAlloc,
|
||
|
AdditionalBlocks,
|
||
|
OriginalIndex + OriginalSizeBlock,
|
||
|
TRUE
|
||
|
);
|
||
|
|
||
|
//
|
||
|
// If there was space, return success
|
||
|
//
|
||
|
if (Success) {
|
||
|
*NewAddress = OriginalAddress;
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Try to allocate space at the beginning of the block
|
||
|
//
|
||
|
Success = AllocateChunkAt(
|
||
|
SubAlloc,
|
||
|
AdditionalBlocks,
|
||
|
OriginalIndex - AdditionalBlocks,
|
||
|
TRUE
|
||
|
);
|
||
|
|
||
|
if (Success) {
|
||
|
//
|
||
|
// Move the data
|
||
|
//
|
||
|
// N.B. We can't just call RtlMoveMemory,
|
||
|
// because we don't know the correspondence
|
||
|
// between the address space we manage, and
|
||
|
// real linear addresses. In addition, for
|
||
|
// risc NTVDM, some additional work may have
|
||
|
// to be done (such as flushing caches).
|
||
|
//
|
||
|
SubAlloc->MoveMemRoutine(
|
||
|
BLOCK_INDEX_TO_ADDRESS(
|
||
|
SubAlloc,
|
||
|
(OriginalIndex - AdditionalBlocks)
|
||
|
),
|
||
|
OriginalAddress,
|
||
|
OriginalSize
|
||
|
);
|
||
|
|
||
|
*NewAddress = BLOCK_INDEX_TO_ADDRESS(
|
||
|
SubAlloc,
|
||
|
(OriginalIndex - AdditionalBlocks)
|
||
|
);
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Attempt to allocate a new block
|
||
|
//
|
||
|
Success = SAAllocate(
|
||
|
SubAlloc,
|
||
|
NewSize,
|
||
|
&Address
|
||
|
);
|
||
|
|
||
|
if (Success) {
|
||
|
//
|
||
|
// Move the data
|
||
|
//
|
||
|
// N.B. We could copy the data, but it would
|
||
|
// require one more function pointer.
|
||
|
//
|
||
|
SubAlloc->MoveMemRoutine(
|
||
|
Address,
|
||
|
OriginalAddress,
|
||
|
OriginalSize
|
||
|
);
|
||
|
|
||
|
SAFree(
|
||
|
SubAlloc,
|
||
|
OriginalSize,
|
||
|
OriginalAddress
|
||
|
);
|
||
|
|
||
|
//
|
||
|
// Indicate success
|
||
|
//
|
||
|
*NewAddress = Address;
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// All reallocation strategies failed.
|
||
|
//
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
BOOL
|
||
|
AllocateChunkAt(
|
||
|
PSUBALLOCATIONDATA SubAlloc,
|
||
|
ULONG Size,
|
||
|
ULONG BlockIndex,
|
||
|
BOOLEAN CheckFree
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This routine attempts to allocate the specified chunk
|
||
|
of memory. It first checks to make sure that it is
|
||
|
free.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
SubAlloc -- Supplies a pointer to the suballocation data
|
||
|
Size -- Supplies the size of the chunk to allocate
|
||
|
BlockIndex -- Supplies the index of the beginning of the block
|
||
|
to allocate
|
||
|
CheckFree -- Supplies an indication of whether to check and see
|
||
|
if the memory is free. If this routine is called from
|
||
|
SAAllocate, we know the memory is free.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
True if successful
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
ULONG i;
|
||
|
|
||
|
if (CheckFree) {
|
||
|
//
|
||
|
// Verify that the memory is free
|
||
|
//
|
||
|
for (i = 0; i < Size; i++){
|
||
|
//
|
||
|
// Insure we don't walk off the end of the data structure
|
||
|
//
|
||
|
if ((i + BlockIndex) >= SubAlloc->Size){
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Check to see if this chunk is free
|
||
|
//
|
||
|
if (
|
||
|
GET_BIT_FROM_CHAR_ARRAY(
|
||
|
SubAlloc->Allocated,
|
||
|
i + BlockIndex
|
||
|
)
|
||
|
== 0
|
||
|
){
|
||
|
continue;
|
||
|
} else {
|
||
|
//
|
||
|
// Chunk is not free
|
||
|
//
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// If the chunk is not free
|
||
|
//
|
||
|
if (i != Size) {
|
||
|
return FALSE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Commit the chunk
|
||
|
//
|
||
|
if (!CommitChunk(SubAlloc, BlockIndex, Size, SACommit)) {
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Mark it as allocated
|
||
|
//
|
||
|
for (i = BlockIndex; i < BlockIndex + Size; i++) {
|
||
|
SET_BIT_IN_CHAR_ARRAY(SubAlloc->Allocated, i);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Update the pointer to the first free block
|
||
|
//
|
||
|
if (BlockIndex == SubAlloc->FirstFree) {
|
||
|
SubAlloc->FirstFree += Size;
|
||
|
}
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOL
|
||
|
FreeChunk(
|
||
|
PSUBALLOCATIONDATA SubAlloc,
|
||
|
ULONG Size,
|
||
|
ULONG BlockIndex
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This routine actually marks the memory as free
|
||
|
and decommits it as necessary.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
SubAlloc -- Supplies a pointer to the suballocation data
|
||
|
Size -- Supplies the size (in SUBALLOC_GRANULARITY) of the
|
||
|
region to free
|
||
|
BlockIndex -- Supplies the index of the begining of the region
|
||
|
(in SUBALLOC_GRANULARITY)
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
TRUE if successful.
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
SUBALLOCATIONDATA LocalSubAlloc;
|
||
|
ULONG CurrentBlock;
|
||
|
BOOL Success;
|
||
|
|
||
|
//
|
||
|
// Save a copy of the suballoc data
|
||
|
//
|
||
|
LocalSubAlloc = *SubAlloc;
|
||
|
|
||
|
//
|
||
|
// reset free pointer
|
||
|
//
|
||
|
if (BlockIndex < SubAlloc->FirstFree) {
|
||
|
SubAlloc->FirstFree = BlockIndex;
|
||
|
}
|
||
|
//
|
||
|
// Mark the region as free
|
||
|
//
|
||
|
// N.B. We mark the block as free before decommitting it, because
|
||
|
// the decommit code will use the allocated bits to determine which
|
||
|
// parts can be decommitted.
|
||
|
//
|
||
|
for (CurrentBlock = BlockIndex;
|
||
|
CurrentBlock < BlockIndex + Size;
|
||
|
CurrentBlock++
|
||
|
) {
|
||
|
CLEAR_BIT_IN_CHAR_ARRAY(SubAlloc->Allocated, CurrentBlock);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Decommit the memory
|
||
|
//
|
||
|
Success = CommitChunk(SubAlloc, BlockIndex, Size, SADecommit);
|
||
|
|
||
|
if (!Success) {
|
||
|
*SubAlloc = LocalSubAlloc;
|
||
|
}
|
||
|
|
||
|
return Success;
|
||
|
}
|
||
|
|
||
|
BOOL
|
||
|
CommitChunk(
|
||
|
PSUBALLOCATIONDATA SubAlloc,
|
||
|
ULONG StartChunk,
|
||
|
ULONG Size,
|
||
|
COMMIT_ACTION Action
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This routine commits a chunk of memory. Part or all
|
||
|
of the specified chunk may already be commited.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
SubAllocation -- Supplies a pointer to the suballocation data
|
||
|
StartChunk -- Supplies the relative start of the region to be
|
||
|
committed (in SUBALLOCATION_GRANULARITY)
|
||
|
Size -- Supplies the size of the chunk to be commited
|
||
|
(in SUBALLOCATION_GRANULARITY)
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
TRUE -- If the block was successfully committed (or already committed)
|
||
|
FALSE -- Otherwise
|
||
|
|
||
|
Notes:
|
||
|
|
||
|
This routine depends on the allocated bits in SubAlloc to determine
|
||
|
whether memory is committed. When memory is to be committed, CommitBlock
|
||
|
must be called before the Allocated bits are modified. When memory is
|
||
|
decommitted, the Allocated bits must be modified before CommitBlock is
|
||
|
called.
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
ULONG FirstBlock, LastBlock, CurrentBlock;
|
||
|
NTSTATUS Status;
|
||
|
|
||
|
ASSERT_STEPTHROUGH;
|
||
|
|
||
|
ASSERT_SUBALLOC(SubAlloc);
|
||
|
|
||
|
//
|
||
|
// Round Start down to next COMMIT_GRANULARITY and convert to block #
|
||
|
//
|
||
|
FirstBlock = (StartChunk * SUBALLOC_GRANULARITY) / COMMIT_GRANULARITY;
|
||
|
|
||
|
//
|
||
|
// Round StartChunk + size up to next COMMIT_GRANULARITY
|
||
|
//
|
||
|
LastBlock = ((StartChunk + Size) * SUBALLOC_GRANULARITY +
|
||
|
(COMMIT_GRANULARITY - 1)) / COMMIT_GRANULARITY;
|
||
|
|
||
|
for (
|
||
|
CurrentBlock = FirstBlock;
|
||
|
CurrentBlock < LastBlock;
|
||
|
CurrentBlock++
|
||
|
) {
|
||
|
|
||
|
//
|
||
|
// If the block is not committed, either commit it or decommit it,
|
||
|
// depending on the value of Action.
|
||
|
//
|
||
|
if (!IsBlockCommitted(SubAlloc, CurrentBlock)) {
|
||
|
if (Action == SACommit) {
|
||
|
|
||
|
Status = (SubAlloc->CommitRoutine)(
|
||
|
CurrentBlock * COMMIT_GRANULARITY + SubAlloc->BaseAddress,
|
||
|
COMMIT_GRANULARITY
|
||
|
);
|
||
|
|
||
|
} else if (Action == SADecommit) {
|
||
|
|
||
|
Status = (SubAlloc->DecommitRoutine)(
|
||
|
CurrentBlock * COMMIT_GRANULARITY + SubAlloc->BaseAddress,
|
||
|
COMMIT_GRANULARITY
|
||
|
);
|
||
|
|
||
|
}
|
||
|
if (Status != STATUS_SUCCESS) {
|
||
|
//
|
||
|
// Bugbug -- decommit any blocks committed here
|
||
|
//
|
||
|
return FALSE;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
BOOL
|
||
|
IsBlockCommitted(
|
||
|
PSUBALLOCATIONDATA SubAlloc,
|
||
|
ULONG Block
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This routine checks to see if a particular block of the
|
||
|
suballocation is committed.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
SubAlloc -- Supplies a pointer to the suballocation data
|
||
|
Block -- Supplies the number of the block to check
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
TRUE -- if the block is committed
|
||
|
FALSE -- if the block is not committed
|
||
|
|
||
|
Notes:
|
||
|
|
||
|
The return value is based on the state of the bits in the
|
||
|
suballocation data, not on information from the NT memory
|
||
|
manager.
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
BOOL Committed = FALSE;
|
||
|
ULONG i;
|
||
|
|
||
|
ASSERT_STEPTHROUGH;
|
||
|
ASSERT_SUBALLOC(SubAlloc);
|
||
|
|
||
|
//
|
||
|
// Check the bits for each of the suballoc blocks in the
|
||
|
// commit block
|
||
|
//
|
||
|
for (i = 0; i < COMMIT_GRANULARITY / SUBALLOC_GRANULARITY; i++) {
|
||
|
|
||
|
//
|
||
|
// Check to see if this suballoc block is allocated
|
||
|
//
|
||
|
if (
|
||
|
GET_BIT_FROM_CHAR_ARRAY(
|
||
|
SubAlloc->Allocated,
|
||
|
i + Block * COMMIT_GRANULARITY / SUBALLOC_GRANULARITY
|
||
|
)
|
||
|
) {
|
||
|
Committed = TRUE;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return Committed;
|
||
|
}
|
||
|
|