/*---------------------------------------------------------------------------* Project: NintendoWare File: ut_FrameHeap.cpp Copyright (C)2009-2011 Nintendo/HAL Laboratory, Inc. All rights reserved. These coded instructions, statements, and computer programs contain proprietary information of Nintendo and/or its licensed developers and are protected by national and international copyright laws. 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. The content herein is highly confidential and should be handled accordingly. $Revision: $ *---------------------------------------------------------------------------*/ #include "precompiled.h" #include #include // for GetOffsetFromPtr(), etc... #include namespace nw { namespace ut { /*---------------------------------------------------------------------------* Name: Create Description: フレームヒープを作成します。 Arguments: startAddress: ヒープ領域の先頭アドレス。 size: ヒープ領域のサイズ。 optFlag: オプションフラグ。 MEM_HEAP_OPT_0_CLEAR - メモリ確保時の0クリアフラグ MEM_HEAP_OPT_DEBUG_FILL - デバッグフィルフラグ Returns: 関数が成功した場合、作成されたフレームヒープのハンドルが返ります。 関数が失敗すると、MEM_INVALID_HEAP_HANDLE が返ります。 Memo: 基本はスレッドセーフではない。 スレッドセーフにする場合、ヒープの属性を指定する引数を追加するようにするか、 あるいは、属性をセットする関数で制御してもらうか。 *---------------------------------------------------------------------------*/ FrameHeap* FrameHeap::Create( void* startAddress, u32 size, u16 optFlag ) { void* endAddress; NW_ASSERT( startAddress != NULL ); endAddress = RoundDown( AddOffsetToPtr( startAddress, size ), MIN_ALIGNMENT ); startAddress = RoundUp( startAddress, MIN_ALIGNMENT ); // start/end がおかしかったり、FrameHeap サイズに足りない場合は NULL を返す if ( ComparePtr( startAddress, endAddress ) > 0 || GetOffsetFromPtr( startAddress, endAddress ) < sizeof(FrameHeap) ) { return NULL; } // フレームヒープ初期化 { FrameHeap* pHeap = new( startAddress ) FrameHeap; pHeap->Initialize( FRMHEAP_SIGNATURE, AddOffsetToPtr( pHeap, sizeof(FrameHeap) ), // heapStart endAddress, // heapEnd optFlag ); pHeap->mHeadAllocator = pHeap->GetHeapStart(); pHeap->mTailAllocator = pHeap->GetHeapEnd(); pHeap->mpState = NULL; return pHeap; } } /*!--------------------------------------------------------------------------* Name: Destroy @brief フレームヒープを破棄します。 @return 破棄したヒープが占めていた領域へのポインタを返します。 *---------------------------------------------------------------------------*/ void* FrameHeap::Destroy() { NW_ASSERT( IsValid() ); Finalize(); return this; } /*!--------------------------------------------------------------------------* Name: Alloc @brief フレームヒープからメモリブロックを確保します。 メモリブロックのアライメントを指定できます。 アライメント値を負の値で指定すると、ヒープの空き領域を後方から探します。 @param[in] size 確保するメモリブロックのサイズ(バイト単位)。 @param[in] alignment 確保するメモリブロックのアライメント。 ±4, ±8, ±16, ±32, ±64, ±128, ... の値が指定できます。 @return メモリブロックの確保が成功した場合、確保したメモリブロックへの ポインタが返ります。 失敗した場合、NULLが返ります。 *---------------------------------------------------------------------------*/ void* FrameHeap::Alloc( u32 size, int alignment ) { void* memory = NULL; NW_ASSERT( IsValid() ); // アライメントのチェック NW_ASSERT( alignment % MIN_ALIGNMENT == 0 ); NW_ASSERT( (Abs(alignment) & (Abs(alignment) - 1)) == 0 ); NW_ASSERT( MIN_ALIGNMENT <= Abs(alignment) ); // size 補正 if ( size == 0 ) { size = 1; } size = RoundUp( size, MIN_ALIGNMENT ); LockHeap(); if ( alignment >= 0 ) // ヒープ先頭から確保 { memory = AllocFromHead( size, alignment ); } else // ヒープ後ろから確保 { memory = AllocFromTail( size, -alignment ); } UnlockHeap(); return memory; } /*!--------------------------------------------------------------------------* Name: AllocFromHead @brief ヒープの先頭からメモリブロックを確保します。 アラインメントの指定があります。 @param[in] size 確保するメモリブロックのサイズ。 @param[in] alignment アライメント値。 @return メモリブロックの確保が成功した場合、確保したメモリブロックへの ポインタが返ります。 失敗した場合、NULLが返ります。 *---------------------------------------------------------------------------*/ void* FrameHeap::AllocFromHead( u32 size, int alignment ) { void* newBlock = RoundUp( mHeadAllocator, (u32)alignment ); void* endAddress = AddOffsetToPtr( newBlock, size ); if ( GetIntPtr( endAddress ) > GetIntPtr( mTailAllocator ) ) { return NULL; } FillAllocMemory( mHeadAllocator, (u32)GetOffsetFromPtr( mHeadAllocator, endAddress ) ); mHeadAllocator = endAddress; return newBlock; } /*!--------------------------------------------------------------------------* Name: AllocFromTail @brief ヒープの末尾からメモリブロックを確保します。 アラインメントの指定があります。 @param[in] size 確保するメモリブロックのサイズ。 @param[in] alignment アライメント値。 @return メモリブロックの確保が成功した場合、確保したメモリブロックへの ポインタが返ります。 失敗した場合、NULLが返ります。 *---------------------------------------------------------------------------*/ void* FrameHeap::AllocFromTail( u32 size, int alignment ) { void* newBlock = RoundDown( AddOffsetToPtr( mTailAllocator, -static_cast(size) ), static_cast(alignment) ); if ( GetIntPtr( newBlock ) < GetIntPtr( mHeadAllocator ) ) { return NULL; } FillAllocMemory( newBlock, (u32)GetOffsetFromPtr( newBlock, mTailAllocator ) ); mTailAllocator = newBlock; return newBlock; } /*---------------------------------------------------------------------------* Name: ResizeForMBlock Description: フレームヒープから確保されたメモリブロックのサイズを変更します。 サイズを変更するメモリブロックは、ヒープの空き領域の前方から確保された 末尾のメモリブロックである必要があります。 Arguments: memBlock: サイズを変更するメモリブロックへのポインタ。 newSize: 新しく割り当てるサイズ(バイト単位)。 4未満の値を指定された場合は、4が指定されたものとして処理します。 Returns: 関数が成功した場合、変更されたメモリブロックのサイズを返します(バイト単位)。 関数が失敗した場合、0 が返ります。 *---------------------------------------------------------------------------*/ u32 FrameHeap::ResizeForMBlock( void* memBlock, u32 newSize ) { NW_ASSERT( IsValid() ); // 最低限、最小アライメントの境界にあるかチェック NW_ASSERT( memBlock == RoundDown( memBlock, MIN_ALIGNMENT ) ); // メモリブロックは前方に存在すること NW_ASSERT( ComparePtr( mHeapStart, memBlock ) <= 0 && ComparePtr( mHeadAllocator, memBlock ) > 0 ); // 状態保存がメモリブロックの後方に無いこと NW_ASSERT( mpState == NULL || ComparePtr( mpState, memBlock ) < 0 ); // newSize == 0 は認めない。 // 0 だと、memBlock が指すメモリブロックが存在しなくなるため。 if ( newSize == 0 ) { newSize = 1; } newSize = RoundUp( newSize, MIN_ALIGNMENT ); LockHeap(); { const int oldSize = GetOffsetFromPtr( memBlock, mHeadAllocator ); void* endAddress = AddOffsetToPtr( memBlock, newSize ); // 同じ場合はなにもしない if ( newSize != oldSize ) { // 拡大するとき if ( newSize > static_cast(oldSize) ) { // サイズが足りない if ( ComparePtr( endAddress, mTailAllocator ) > 0 ) { newSize = 0; } else { FillAllocMemory( mHeadAllocator, newSize - oldSize ); } } // 縮小するとき else { FillFreeMemory( endAddress, oldSize - newSize ); } mHeadAllocator = endAddress; } } UnlockHeap(); return newSize; } /*!--------------------------------------------------------------------------* Name: GetAllocatableSize @brief フレームヒープ内の割り当て可能な最大サイズを取得します。 メモリブロックのアライメントを指定できます。 @param[in] alignment 確保するメモリブロックのアライメント。 ±4, ±8, ±16, ±32, ±64, ±128, ... の値が指定できます。 @return フレームヒープ内の割り当て可能な最大サイズを返します(バイト単位)。 *---------------------------------------------------------------------------*/ u32 FrameHeap::GetAllocatableSize( int alignment ) { NW_ASSERT( IsValid() ); NW_ASSERT( alignment % MIN_ALIGNMENT == 0 ); NW_ASSERT( (Abs( alignment ) & (Abs( alignment ) - 1)) == 0 ); NW_ASSERT( MIN_ALIGNMENT <= Abs( alignment ) ); alignment = Abs( alignment ); { u32 retVal; #ifdef NW_UT_HEAP_MULTITHREADED bool enabled = OS_DisableIrq(); #endif const void* block = RoundUp( mHeadAllocator, (u32)alignment ); if ( GetIntPtr( block ) > GetIntPtr( mTailAllocator ) ) { retVal = 0; } else { retVal = (u32)GetOffsetFromPtr( block, mTailAllocator ); } #ifdef NW_UT_HEAP_MULTITHREADED OS_RestoreIrq( enabled ); #endif return retVal; } } /*!--------------------------------------------------------------------------* Name: Free @brief フレームヒープへメモリブロックを返却します。 @param[in] mode メモリブロックの返却方法。 @return なし。 *---------------------------------------------------------------------------*/ void FrameHeap::Free( int mode ) { NW_ASSERT( IsValid() ); LockHeap(); if ( mode & FREE_HEAD ) { FreeHead(); } if ( mode & FREE_TAIL ) { FreeTail(); } UnlockHeap(); } /*!--------------------------------------------------------------------------* Name: FreeHead @brief ヒープ領域の先頭から確保したメモリブロックを一括して開放します。 @return なし。 *---------------------------------------------------------------------------*/ void FrameHeap::FreeHead() { NW_ASSERT( ComparePtr( mHeadAllocator, mHeapStart ) >= 0 ); FillFreeMemory( mHeapStart, (u32)GetOffsetFromPtr( mHeapStart, mHeadAllocator ) ); mHeadAllocator = mHeapStart; mpState = NULL; } /*!--------------------------------------------------------------------------* Name: FreeTail @brief ヒープ領域の末尾から確保したメモリブロックを一括して開放します。 @return なし。 *---------------------------------------------------------------------------*/ void FrameHeap::FreeTail() { NW_ASSERT( ComparePtr( mHeapEnd, mTailAllocator ) >= 0 ); FillFreeMemory( mTailAllocator, (u32)GetOffsetFromPtr( mTailAllocator, mHeapEnd ) ); // ヒープの割り当て状態の復帰により、解放したメモリブロックが復活してしまわ // ないように、保存状態の交尾割り当てポインタを再設定しておく for ( HeapState* pState = mpState; pState; pState = pState->pPrevState ) { pState->tailAllocator = mHeapEnd; } mTailAllocator = mHeapEnd; } /*!--------------------------------------------------------------------------* Name: RecordState @brief フレームヒープの現在のメモリ使用状態を記録します。 後で記録したメモリ使用状況に戻すことができます。 状態を記録するのに20バイト使用します。 @param[in] tagName 状態記録に付けるタグ名。 @return 関数が成功した場合、true が返ります。 失敗したら、false が返ります。 *---------------------------------------------------------------------------*/ bool FrameHeap::RecordState( u32 tagName ) { bool retVal; NW_ASSERT( IsValid() ); LockHeap(); { void* oldHeadAllocator = mHeadAllocator; void* stateHeap = AllocFromHead( sizeof(HeapState), MIN_ALIGNMENT ); if ( stateHeap == NULL ) { retVal = false; } else { HeapState* pState = new( stateHeap ) HeapState; if ( pState == NULL ) { retVal = false; } else { pState->tagName = tagName; pState->headAllocator = oldHeadAllocator; pState->tailAllocator = mTailAllocator; pState->pPrevState = mpState; mpState = pState; retVal = true; } } } UnlockHeap(); return retVal; } /*---------------------------------------------------------------------------* Name: FreeByState Description: フレームヒープのメモリブロックを記録された状態に従って返却します。 指定したタグ名を用いてRecordStateForFrmHeap()を呼び出す直前 のメモリの使用状況に戻ります。 タグ名に0を指定すると最後にRecordStateForFrmHeap()を呼び出す 直前の状態になります。 タグ名を指定して返却した場合、それ以後に呼び出された RecordStateForFrmHeap()によって記録された情報は 無くなります。 Arguments: tagName: 状態記録に付けるタグ名。 Returns: 関数が成功した場合、true が返ります。 失敗したら、false が返ります。 *---------------------------------------------------------------------------*/ bool FrameHeap::FreeByState( u32 tagName ) { bool retVal; NW_ASSERT( IsValid() ); LockHeap(); { HeapState* pState = mpState; // タグ検索 if ( tagName != 0 ) { for ( ; pState; pState = pState->pPrevState ) { if ( pState->tagName == tagName ) { break; } } } if ( pState == NULL ) { retVal = false; } else { void* oldHeadAllocator = mHeadAllocator; void* oldTailAllocator = mTailAllocator; mHeadAllocator = pState->headAllocator; mTailAllocator = pState->tailAllocator; mpState = pState->pPrevState; FillFreeMemory( mHeadAllocator, (u32)GetOffsetFromPtr( mHeadAllocator, oldHeadAllocator ) ); FillFreeMemory( oldTailAllocator, (u32)GetOffsetFromPtr( oldTailAllocator, mTailAllocator ) ); retVal = true; } } UnlockHeap(); return retVal; } /*!--------------------------------------------------------------------------* Name: Adjust @brief フレームヒープの空き領域をヒープ領域から開放し、その分ヒープ領域を 縮小します。 ヒープメモリの後ろから確保されたメモリブロックが存在していては いけません。 @return 関数が成功した場合、縮小後のフレームヒープのサイズを返します (バイト単位)。 失敗した場合、0を返します。 *---------------------------------------------------------------------------*/ u32 FrameHeap::Adjust() { NW_ASSERT( IsValid() ); u32 retVal; LockHeap(); // 後方から確保されたメモリブロックがある場合は失敗 if ( 0 < GetOffsetFromPtr( mTailAllocator, mHeapEnd ) ) { retVal = 0; } else { mTailAllocator = mHeadAllocator; mHeapEnd = mHeadAllocator; retVal = (u32)GetOffsetFromPtr( this, mHeapEnd ); } UnlockHeap(); return retVal; } } // nw::ut } // nw