/*---------------------------------------------------------------------------* Project: MEM library File: mem_frameHeap.c Programmers: Takano Makoto Copyright 2005 Nintendo. All rights reserved. These coded instructions, statements, and computer programs contain proprietary information of Nintendo of America Inc. and/or Nintendo Company Ltd., and are protected by Federal copyright law. They may not be disclosed to third parties or copied or duplicated in any form, in whole or in part, without the prior written consent of Nintendo. *---------------------------------------------------------------------------*/ #include #include "heapCommoni.h" #include /* ======================================================================== Macro Constants ======================================================================== */ // Minimum alignment value #define MIN_ALIGNMENT 4 /* ======================================================================== static functions ======================================================================== */ /*---------------------------------------------------------------------------* Name: IsValidFrmHeapHandle_ Description: Checks the signature to determine whether the frame heap is valid. Arguments: handle: The heap handle Returns: TRUE: The heap handle is valid FALSE: The heap handle is not valid *---------------------------------------------------------------------------*/ static inline BOOL IsValidFrmHeapHandle_( MEMHeapHandle handle ) { if ( handle == MEM_HEAP_INVALID_HANDLE ) { return FALSE; } { MEMiHeapHead* pHeapHd = handle; return pHeapHd->signature == MEMi_FRMHEAP_SIGNATURE; } } /*---------------------------------------------------------------------------* Name: GetFrmHeapHeadPtrFromHeapHead_ Description: Gets pointer to frame heap header from pointer to heap header. Arguments: pHHead: Pointer to the heap header. Returns: Returns the pointer to the frame heap header. *---------------------------------------------------------------------------*/ static inline MEMiFrmHeapHead* GetFrmHeapHeadPtrFromHeapHead_( MEMiHeapHead* pHHead ) { return (MEMiFrmHeapHead*)AddU32ToPtr( pHHead, sizeof(MEMiHeapHead) ); } /*---------------------------------------------------------------------------* Name: GetHeapHeadPtrFromFrmHeapHead_ Description: Gets heap header pointer from frame heap header pointer. Arguments: pFrmHeapHd: frame heap header pointer. Returns: Returns the pointer to the heap header. *---------------------------------------------------------------------------*/ static inline MEMiHeapHead* GetHeapHeadPtrFromFrmHeapHead_( MEMiFrmHeapHead* pFrmHeapHd ) { return (MEMiHeapHead*)SubU32ToPtr(pFrmHeapHd, sizeof(MEMiHeapHead)); } /*---------------------------------------------------------------------------* Name: InitFrameHeap_ Description: Initializes the frame heap. Arguments: startAddress: Starting address of memory used as frame heap. endAddress: Ending address + 1 of the memory to be used as the frame heap. optFlag: Option flag. Returns: Returns the pointer to the heap header. *---------------------------------------------------------------------------*/ static MEMiHeapHead* InitFrameHeap_( void* startAddress, void* endAddress, u16 optFlag ) { MEMiHeapHead* pHeapHd = (MEMiHeapHead*)startAddress; MEMiFrmHeapHead* pFrmHeapHd = GetFrmHeapHeadPtrFromHeapHead_( pHeapHd ); MEMiInitHeapHead( // Heap common initialization pHeapHd, MEMi_FRMHEAP_SIGNATURE, AddU32ToPtr( pFrmHeapHd, sizeof(MEMiFrmHeapHead) ), // heapStart endAddress, // heapEnd optFlag ); pFrmHeapHd->headAllocator = pHeapHd->heapStart; pFrmHeapHd->tailAllocator = pHeapHd->heapEnd; pFrmHeapHd->pState = NULL; // State saving state location return pHeapHd; } /*---------------------------------------------------------------------------* Name: AllocFromHead_ Description: Allocates the memory block from the top of the heap. There is an alignment specification. Arguments: pHHead: Pointer to the heap header. size: Size of the memory block to be allocated. alignment: Alignment value. Returns: Returns the pointer to the allocated memory block if the memory block was successfully allocated. If the operation fails, NULL is returned. *---------------------------------------------------------------------------*/ static void* AllocFromHead_( MEMiFrmHeapHead* pFrmHeapHd, u32 size, int alignment ) { void* newBlock = RoundUpPtr(pFrmHeapHd->headAllocator, alignment); void* endAddress = AddU32ToPtr(newBlock, size); if ( GetUIntPtr(endAddress) > GetUIntPtr(pFrmHeapHd->tailAllocator) ) { return NULL; } FillAllocMemory( // Fill memory GetHeapHeadPtrFromFrmHeapHead_(pFrmHeapHd), pFrmHeapHd->headAllocator, GetOffsetFromPtr(pFrmHeapHd->headAllocator, endAddress)); pFrmHeapHd->headAllocator = endAddress; return newBlock; } /*---------------------------------------------------------------------------* Name: AllocFromTail_ Description: Allocates a memory block from the end of the heap. There is an alignment specification. Arguments: pHHead: Pointer to the heap header. size: Size of the memory block to be allocated. alignment: Alignment value. Returns: Returns the pointer to the allocated memory block if the memory block was successfully allocated. If the operation fails, NULL is returned. *---------------------------------------------------------------------------*/ static void* AllocFromTail_( MEMiFrmHeapHead* pFrmHeapHd, u32 size, int alignment ) { void* newBlock = RoundDownPtr(SubU32ToPtr(pFrmHeapHd->tailAllocator, size), alignment); if ( GetUIntPtr(newBlock) < GetUIntPtr(pFrmHeapHd->headAllocator) ) { return NULL; } FillAllocMemory( // Fill memory GetHeapHeadPtrFromFrmHeapHead_(pFrmHeapHd), newBlock, GetOffsetFromPtr(newBlock, pFrmHeapHd->tailAllocator) ); pFrmHeapHd->tailAllocator = newBlock; return newBlock; } /*---------------------------------------------------------------------------* Name: FreeHead_ Description: Deallocates memory blocks allocated from the head of the heap area all at once. Arguments: pHeapHd: heap header pointer. Returns: None. *---------------------------------------------------------------------------*/ static void FreeHead_( MEMiHeapHead* pHeapHd ) { MEMiFrmHeapHead* pFrmHeapHd = GetFrmHeapHeadPtrFromHeapHead_(pHeapHd); FillFreeMemory( pHeapHd, pHeapHd->heapStart, GetOffsetFromPtr(pHeapHd->heapStart, pFrmHeapHd->headAllocator) ); pFrmHeapHd->headAllocator = pHeapHd->heapStart; pFrmHeapHd->pState = NULL; } /*---------------------------------------------------------------------------* Name: FreeTail_ Description: Deallocates the memory blocks allocated from the heap area all at once. Arguments: pHeapHd: heap header pointer. Returns: None. *---------------------------------------------------------------------------*/ static void FreeTail_( MEMiHeapHead* pHeapHd ) { MEMiFrmHeapHead* pFrmHeapHd = GetFrmHeapHeadPtrFromHeapHead_(pHeapHd); FillFreeMemory( pHeapHd, pFrmHeapHd->tailAllocator, GetOffsetFromPtr(pFrmHeapHd->tailAllocator, pHeapHd->heapEnd) ); /* Reset the save data end allocation pointer to avoid restoring deallocated memory blocks resulting from the recovery of the heap allocation status. */ { MEMiFrmHeapState* pState; for ( pState = pFrmHeapHd->pState; pState; pState = pState->pPrevState ) { pState->tailAllocator = pHeapHd->heapEnd; } } pFrmHeapHd->tailAllocator = pHeapHd->heapEnd; } /*---------------------------------------------------------------------------* Name: PrintSize_ Description: Output size and percentage. Arguments: size: Target size. wholeSize: Whole size. Returns: None. *---------------------------------------------------------------------------*/ #if defined(_DEBUG) static void PrintSize_( u32 size, u32 wholeSize ) { OSReport("%9d (%6.2f%%)", size, 100.0 * size / wholeSize); } // #if defined(_DEBUG) #endif /* ======================================================================== External Functions (Non-Public) ======================================================================== */ /*---------------------------------------------------------------------------* Name: MEMiGetFreeStartForFrmHeap Description: Gets the head address of the free area of the frame heap. Arguments: heap: Handle for the frame heap Returns: Returns the head address of the free area of the frame heap. *---------------------------------------------------------------------------*/ void* MEMiGetFreeStartForFrmHeap( MEMHeapHandle heap ) { ASSERT(IsValidFrmHeapHandle_(heap)); return GetFrmHeapHeadPtrFromHeapHead_(heap)->headAllocator; } /*---------------------------------------------------------------------------* Name: MEMiGetFreeEndForFrmHeap Description: Gets the end address of the free area of the frame heap. Arguments: heap: Handle for the frame heap Returns: Returns the end address + 1 of the free area of the frame heap. *---------------------------------------------------------------------------*/ void* MEMiGetFreeEndForFrmHeap( MEMHeapHandle heap ) { ASSERT(IsValidFrmHeapHandle_(heap)); return GetFrmHeapHeadPtrFromHeapHead_(heap)->tailAllocator; } /*---------------------------------------------------------------------------* Name: MEMiDumpFrmHeap Description: Displays the information in the frame heap. This function is used for debugging. Arguments: heap: Handle for the frame heap Returns: None. *---------------------------------------------------------------------------*/ #if defined(_DEBUG) void MEMiDumpFrmHeap( MEMHeapHandle heap ) { ASSERT(IsValidFrmHeapHandle_(heap)); { MEMiHeapHead *const pHeapHd = heap; MEMiFrmHeapHead *const pFrmHeapHd = GetFrmHeapHeadPtrFromHeapHead_( pHeapHd ); const u32 heapSize = GetOffsetFromPtr( pHeapHd->heapStart, pHeapHd->heapEnd ); MEMiDumpHeapHead(pHeapHd); OSReport( " head [%p - %p) ", pHeapHd->heapStart, pFrmHeapHd->headAllocator); PrintSize_(GetOffsetFromPtr(pHeapHd->heapStart, pFrmHeapHd->headAllocator), heapSize); OSReport("\n free "); PrintSize_(GetOffsetFromPtr(pFrmHeapHd->headAllocator, pFrmHeapHd->tailAllocator), heapSize); OSReport("\n tail [%p - %p) ", pFrmHeapHd->tailAllocator, pHeapHd->heapEnd); PrintSize_(GetOffsetFromPtr(pFrmHeapHd->tailAllocator, pHeapHd->heapEnd), heapSize); OSReport("\n"); if ( pFrmHeapHd->pState ) { MEMiFrmHeapState* pState; OSReport(" state : [tag] [head] [tail]\n"); for ( pState = pFrmHeapHd->pState; pState; pState = pState->pPrevState ) { OSReport(" '%c%c%c%c' : %p %p\n", pState->tagName >>24, (pState->tagName >>16) & 0xFF, (pState->tagName >>8) & 0xFF, pState->tagName & 0xFF, pState->headAllocator, pState->tailAllocator); } } OSReport("\n"); } } // #if defined(_DEBUG) #endif /* ======================================================================== External functions (public) ======================================================================== */ /*---------------------------------------------------------------------------* Name: MEMCreateFrmHeapEx Description: Creates a frame heap. Arguments: startAddress: Start address of heap area size: Size of heap area optFlag: Option flag. MEM_HEAP_OPT_0_CLEAR - 0 clear flag at memory allocation MEM_HEAP_OPT_DEBUG_FILL - the debug fill flag Returns: Returns the handle for the created frame heap if the function succeeds. MEM_INVALID_HEAP_HANDLE is returned if the function fails. Memo: This is essentially not thread-safe. To make it thread safe, please try to add the argument specifying the heap attribute or suppress the function that sets the attribute. *---------------------------------------------------------------------------*/ MEMHeapHandle MEMCreateFrmHeapEx( void* startAddress, u32 size, u16 optFlag ) { void* endAddress; ASSERT(startAddress != NULL); endAddress = RoundDownPtr(AddU32ToPtr(startAddress, size), MIN_ALIGNMENT); startAddress = RoundUpPtr(startAddress, MIN_ALIGNMENT); if ( GetUIntPtr(startAddress) > GetUIntPtr(endAddress) || GetOffsetFromPtr(startAddress, endAddress) < sizeof(MEMiHeapHead) + sizeof(MEMiFrmHeapHead) ) { return MEM_HEAP_INVALID_HANDLE; } { // Initialize frame heap MEMiHeapHead* pHHead = InitFrameHeap_( startAddress, endAddress, optFlag ); return pHHead; // The pointer to the heap header is used as the handle value. } } /*---------------------------------------------------------------------------* Name: MEMDestroyFrmHeap Description: Destroys the frame heap. Arguments: heap: Handle for the frame heap Returns: Returns a pointer to the region occupied by the destroyed heap. *---------------------------------------------------------------------------*/ void* MEMDestroyFrmHeap( MEMHeapHandle heap ) { ASSERT(IsValidFrmHeapHandle_(heap)); MEMiFinalizeHeap(heap); return (void*)heap; } /*---------------------------------------------------------------------------* Name: MEMAllocFromFrmHeapEx Description: Allocates a memory block from the frame heap. The memory block alignment can be specified. If a negative alignment value is specified, an available region is searched for from the back of the heap. Arguments: heap: Handle for the frame heap size: Size of the memory block to be allocated (in bytes) alignment: Alignment of the memory block to be allocated The following values can be specified: + or - 4, + or - 8, + or - 16, + or - 32, + or - 64, + or - 128, ... Returns: Returns the pointer to the allocated memory block if the memory block was successfully allocated. If the operation fails, NULL is returned. *---------------------------------------------------------------------------*/ void* MEMAllocFromFrmHeapEx( MEMHeapHandle heap, u32 size, int alignment ) { void* memory = NULL; MEMiFrmHeapHead* pFrmHeapHd; ASSERT(IsValidFrmHeapHandle_(heap)); // alignment check ASSERT(alignment % MIN_ALIGNMENT == 0); ASSERT((abs(alignment) & (abs(alignment) - 1)) == 0); ASSERT(MIN_ALIGNMENT <= abs(alignment)); pFrmHeapHd = GetFrmHeapHeadPtrFromHeapHead_(heap); if ( size == 0 ) { size = 1; } size = RoundUp(size, MIN_ALIGNMENT); LockHeap( heap ); if ( alignment >= 0 ) // Allocate from the front of the heap { memory = AllocFromHead_(pFrmHeapHd, size, alignment); } else // Allocate from the end of the heap { memory = AllocFromTail_(pFrmHeapHd, size, -alignment); } UnlockHeap( heap ); return memory; } /*---------------------------------------------------------------------------* Name: MEMFreeToFrmHeap Description: This function returns the memory block to the frame heap. Arguments: heap: Handle for the frame heap mode: Method of returning memory block Returns: None. *---------------------------------------------------------------------------*/ void MEMFreeToFrmHeap( MEMHeapHandle heap, int mode ) { ASSERT(IsValidFrmHeapHandle_(heap)); LockHeap( heap ); if (mode & MEM_FRMHEAP_FREE_HEAD) { FreeHead_(heap); } if (mode & MEM_FRMHEAP_FREE_TAIL) { FreeTail_(heap); } UnlockHeap( heap ); } /*---------------------------------------------------------------------------* Name: MEMGetAllocatableSizeForFrmHeapEx Description: Gets the maximum allocatable size in the frame heap. The memory block alignment can be specified. Arguments: heap: Handle for the frame heap alignment: Alignment of the memory block to be allocated The following values can be specified: + or - 4, + or - 8, + or - 16, + or - 32, + or - 64, + or - 128, ... Returns: Returns the maximum allocatable size in the frame heap (in bytes). *---------------------------------------------------------------------------*/ u32 MEMGetAllocatableSizeForFrmHeapEx( MEMHeapHandle heap, int alignment ) { ASSERT(IsValidFrmHeapHandle_(heap)); // alignment check ASSERT(alignment % MIN_ALIGNMENT == 0); ASSERT((abs(alignment) & (abs(alignment) - 1)) == 0); ASSERT(MIN_ALIGNMENT <= abs(alignment)); alignment = abs(alignment); // Convert to a positive value just to be sure { u32 retVal; const MEMiFrmHeapHead* pFrmHeapHd = GetFrmHeapHeadPtrFromHeapHead_(heap); BOOL enabled = OSDisableInterrupts(); const void* block = RoundUpPtr(pFrmHeapHd->headAllocator, alignment); if ( GetUIntPtr(block) > GetUIntPtr(pFrmHeapHd->tailAllocator) ) { retVal = 0; } else { retVal = GetOffsetFromPtr( block, pFrmHeapHd->tailAllocator ); } (void)OSRestoreInterrupts( enabled ); return retVal; } } /*---------------------------------------------------------------------------* Name: MEMRecordStateForFrmHeap Description: This function records the current use status of the frame heap. You can return to the recorded memory usage status later. 20 bytes are used to record the status. Arguments: heap: Handle for the frame heap tagName: Tag name given to the status record. Returns: Returns TRUE if successful. Returns FALSE if unsuccessful. *---------------------------------------------------------------------------*/ BOOL MEMRecordStateForFrmHeap( MEMHeapHandle heap, u32 tagName ) { BOOL retVal; ASSERT(IsValidFrmHeapHandle_(heap)); LockHeap( heap ); { MEMiFrmHeapHead* pFrmHeapHd = GetFrmHeapHeadPtrFromHeapHead_(heap); void* oldHeadAllocator = pFrmHeapHd->headAllocator; // Allocate memory for saving information. MEMiFrmHeapState* pState = (MEMiFrmHeapState*)AllocFromHead_(pFrmHeapHd, sizeof(MEMiFrmHeapState), MIN_ALIGNMENT); if ( ! pState ) { retVal = FALSE; goto ret_; } // Store the current status pState->tagName = tagName; pState->headAllocator = oldHeadAllocator; pState->tailAllocator = pFrmHeapHd->tailAllocator; pState->pPrevState = pFrmHeapHd->pState; pFrmHeapHd->pState = pState; retVal = TRUE; } ret_: UnlockHeap( heap ); return retVal; } /*---------------------------------------------------------------------------* Name: MEMFreeByStateToFrmHeap Description: This function returns the frame heap memory block based on the recorded status. This function uses the specified tag name to restore the memory usage status in effect immediately prior to calling RecordStateForFrmHeap(). If 0 is specified for the tag name the status is returned to that in effect the last time RecordStateForFrmHeap() was called. When a memory block is returned by specifying a tag name, the information recorded with RecordStateForFrmHeap() which is called later will disappear. Arguments: heap: Handle for the frame heap tagName: Tag name given to the status record. Returns: Returns TRUE if successful. Returns FALSE if unsuccessful. *---------------------------------------------------------------------------*/ BOOL MEMFreeByStateToFrmHeap( MEMHeapHandle heap, u32 tagName ) { BOOL retVal; ASSERT(IsValidFrmHeapHandle_(heap)); LockHeap( heap ); { MEMiFrmHeapHead* pFrmHeapHd = GetFrmHeapHeadPtrFromHeapHead_(heap); MEMiFrmHeapState* pState = pFrmHeapHd->pState; if ( tagName != 0 ) // The tag name specification exists { for ( ; pState; pState = pState->pPrevState ) { if ( pState->tagName == tagName ) { break; } } } if ( ! pState ) { retVal = FALSE; goto ret_; } { void* oldHeadAllocator = pFrmHeapHd->headAllocator; void* oldTailAllocator = pFrmHeapHd->tailAllocator; pFrmHeapHd->headAllocator = pState->headAllocator; pFrmHeapHd->tailAllocator = pState->tailAllocator; pFrmHeapHd->pState = pState->pPrevState; FillFreeMemory( heap, pFrmHeapHd->headAllocator, GetOffsetFromPtr(pFrmHeapHd->headAllocator, oldHeadAllocator) ); FillFreeMemory( heap, oldTailAllocator, GetOffsetFromPtr(oldTailAllocator, pFrmHeapHd->tailAllocator) ); } retVal = TRUE; } ret_: UnlockHeap( heap ); return retVal; } /*---------------------------------------------------------------------------* Name: MEMAdjustFrmHeap Description: This function deallocates an available region of the frame heap from the heap region and reduces the heap region by that amount. There must not be memory blocks allocated from the back of heap memory. Arguments: heap: Handle for the frame heap Returns: Returns the frame heap size in bytes after reduction if successful. Returns 0 if unsuccessful. *---------------------------------------------------------------------------*/ u32 MEMAdjustFrmHeap( MEMHeapHandle heap ) { ASSERT(IsValidFrmHeapHandle_(heap)); { MEMiHeapHead* pHeapHd = heap; MEMiFrmHeapHead* pFrmHeapHd = GetFrmHeapHeadPtrFromHeapHead_( pHeapHd ); u32 retVal; LockHeap( heap ); // The function fails if there are any memory blocks allocated from the end. if ( 0 < GetOffsetFromPtr( pFrmHeapHd->tailAllocator, pHeapHd->heapEnd ) ) { retVal = 0; goto ret_; } pFrmHeapHd->tailAllocator = pHeapHd->heapEnd = pFrmHeapHd->headAllocator; retVal = GetOffsetFromPtr( heap, pHeapHd->heapEnd ); ret_: UnlockHeap( heap ); return retVal; } } /*---------------------------------------------------------------------------* Name: MEMResizeForMBlockFrmHeap Description: This function changes the size of the memory block allocated from the frame heap. The memory block to be resized must be the end memory block allocated in the front of the available region of the heap. Arguments: heap: Handle for the frame heap memBlock: Pointer to the memory block to be resized. newSize: The new size to be allocated (in bytes). If a value less than 4 is specified, processing is performed as if 4 was specified. Returns: Returns the size of the resized memory block (in bytes) if the function is successful. Returns 0 if the function fails. *---------------------------------------------------------------------------*/ u32 MEMResizeForMBlockFrmHeap( MEMHeapHandle heap, void* memBlock, u32 newSize ) { MEMiHeapHead* pHeapHd = NULL; MEMiFrmHeapHead* pFrmHeapHd = NULL; ASSERT(IsValidFrmHeapHandle_(heap)); ASSERT(memBlock == RoundDownPtr(memBlock, MIN_ALIGNMENT)); // Check if at the minimum alignment boundary. pHeapHd = heap; pFrmHeapHd = GetFrmHeapHeadPtrFromHeapHead_( pHeapHd ); ASSERT( ComparePtr(pHeapHd->heapStart, memBlock) <= 0 && ComparePtr(pFrmHeapHd->headAllocator, memBlock) > 0 ); // Be sure that memory blocks exist at the front ASSERT( pFrmHeapHd->pState == NULL || ComparePtr(pFrmHeapHd->pState, memBlock) < 0 ); // Be sure that there is no status saved after the memory block /* Have set up so newSize cannot be 0. This is because if newSize is somehow 0, the memory block specified by memBlock has ceased to exist. */ if ( newSize == 0 ) { newSize = 1; } newSize = RoundUp( newSize, MIN_ALIGNMENT ); LockHeap( heap ); { const u32 oldSize = GetOffsetFromPtr( memBlock, pFrmHeapHd->headAllocator ); void* endAddress = AddU32ToPtr( memBlock, newSize ); if ( newSize == oldSize ) // If the block size is not changed { goto ret_; } if ( newSize > oldSize ) // During magnification { if ( ComparePtr( endAddress, pFrmHeapHd->tailAllocator ) > 0 ) // If the size is insufficient { newSize = 0; goto ret_; } FillAllocMemory( heap, pFrmHeapHd->headAllocator, newSize - oldSize ); } else // During reduction { FillFreeMemory( heap, endAddress, oldSize - newSize ); } pFrmHeapHd->headAllocator = endAddress; } ret_: UnlockHeap( heap ); return newSize; }