/*---------------------------------------------------------------------------* Project: Horizon File: fnd_FrameHeap.h Copyright (C)2009 Nintendo Co., Ltd. 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. $Rev: 24310 $ *---------------------------------------------------------------------------*/ /*! @file @brief フレームヒープに関するAPIの宣言 */ #ifndef NN_FND_FND_FRAMEHEAP_H_ #define NN_FND_FND_FRAMEHEAP_H_ #include #include #include #include #include #include #ifdef __cplusplus #define NN_FND_FRAMEHEAP_FREE_HEAD 1 #define NN_FND_FRAMEHEAP_FREE_TAIL 2 #define NN_FND_FRAMEHEAP_FREE_ALL (NN_FND_FRAMEHEAP_FREE_HEAD | NN_FND_FRAMEHEAP_FREE_TAIL) #define NN_FND_FRAMEHEAP_ADJUST_TAIL 1 #define NN_FND_FRAMEHEAP_ADJUST_HEAD -1 namespace nn { namespace fnd { /*! @brief フレームヒープのベースクラスです。 このクラスのインスタンスを直接作成することはできません。 */ class FrameHeapBase : public HeapBase { public: class State; protected: FrameHeapBase() : m_Addr(0) {} FrameHeapBase(uptr addr, size_t size, bit32 option = 0) : m_Addr(0) { Initialize(addr, size, option); } template FrameHeapBase(const MemoryBlock& block, bit32 option = 0) : m_Addr(0) { Initialize(block.GetAddress(), block.GetSize(), option); } void Initialize(uptr addr, size_t size, bit32 option = 0); void Finalize() { m_Addr = 0; } virtual ~FrameHeapBase() { Finalize(); } void* Allocate(size_t size, s32 alignment); size_t ResizeBlock(void* p, size_t newSize); void FreeAll(int mode); virtual void FreeV(void*) {} State SaveState(); void RestoreState(State state); MemoryRange Adjust(int mode); size_t GetAllocatableSize(s32 alignment) const; virtual void* GetStartAddress() const { return reinterpret_cast(m_Addr); } virtual size_t GetTotalSize() const { return m_Size; } virtual void Dump() const; virtual bool HasAddress(const void* addr) const; private: uptr m_Addr; size_t m_Size; uptr m_CurrentHead; uptr m_CurrentTail; }; /*! @brief フレームヒープの状態を表すクラスです。 */ class FrameHeapBase::State { public: /*! @brief コンストラクタです。 @param heap フレームヒープを指定します。 */ explicit State(FrameHeapBase& heap) : m_Head(heap.m_CurrentHead), m_Tail(heap.m_CurrentTail) {} /*! @brief コンストラクタです。初期化は行いません。 */ State() : m_Head(0), m_Tail(0) {} private: uptr m_Head; uptr m_Tail; explicit State(uptr head, uptr tail) : m_Head(head), m_Tail(tail) {} friend class FrameHeapBase; }; inline void* FrameHeapBase::Allocate(size_t size, s32 alignment) { NN_TASSERT_(size > 0); NN_TASSERT_(alignment != 0); uptr ret; if ( alignment == 0 ) { NN_TPANIC_("invalid argument: alignment == 0"); } if ( alignment > 0 ) { ret = RoundUp(m_CurrentHead, alignment); uptr current = ret + size; if (current > m_CurrentTail) { return 0; } m_CurrentHead = current; } else { uptr current = ret = RoundDown(m_CurrentTail-size, -alignment); if (current < m_CurrentHead) { return 0; } m_CurrentTail = current; } // フィルオプション DebugFillMemory(ret, size, HEAP_FILL_TYPE_ALLOC); FillMemoryZero(ret, size); return reinterpret_cast(ret); } inline void FrameHeapBase::FreeAll(int mode) { NN_TASSERT_( mode == NN_FND_FRAMEHEAP_FREE_HEAD || mode == NN_FND_FRAMEHEAP_FREE_TAIL || mode == NN_FND_FRAMEHEAP_FREE_ALL); if ( mode & NN_FND_FRAMEHEAP_FREE_HEAD ) { DebugFillMemory(m_Addr, m_CurrentHead - m_Addr, HEAP_FILL_TYPE_FREE); this->m_CurrentHead = this->m_Addr; } if ( mode & NN_FND_FRAMEHEAP_FREE_TAIL ) { DebugFillMemory(m_CurrentTail, m_Addr + m_Size - m_CurrentTail, HEAP_FILL_TYPE_FREE); this->m_CurrentTail = this->m_Addr + this->m_Size; } } inline size_t FrameHeapBase::ResizeBlock(void* p, size_t newSize) { // TODO: debug_fill uptr& addr = reinterpret_cast(p); NN_TASSERT_(m_Addr <= addr && addr < m_CurrentTail); NN_TASSERT_(addr <= m_CurrentHead); uptr current = addr + newSize; if (current < m_CurrentTail) { if ( current < this->m_CurrentHead ) { DebugFillMemory(current, this->m_CurrentHead - current, HEAP_FILL_TYPE_FREE); } if ( this->m_CurrentHead < current ) { FillMemoryZero(this->m_CurrentHead, current - this->m_CurrentHead); } this->m_CurrentHead = current; return newSize; } else { return 0; } } inline MemoryRange FrameHeapBase::Adjust(int mode) { // TODO: debug_fill if ( mode == 0 ) { NN_TPANIC_("invalid argument: mode == 0"); } if ( mode > 0 ) { if ( m_CurrentTail != m_Addr + m_Size ) { NN_TPANIC_("invalid call: tail used"); } DebugFillMemory(m_CurrentHead, m_Addr + m_Size - m_CurrentHead, HEAP_FILL_TYPE_FREE); uptr oldtail = m_Addr + m_Size; this->m_Size = m_CurrentHead - m_Addr; this->m_CurrentTail = m_CurrentHead; return MemoryRange(this->m_CurrentTail, oldtail); } else { if ( m_CurrentHead != m_Addr ) { NN_TPANIC_("invalid call: head used"); } DebugFillMemory(m_Addr, m_CurrentTail - m_Addr, HEAP_FILL_TYPE_FREE); uptr oldhead = m_Addr; this->m_Size = m_Addr + m_Size - m_CurrentTail; this->m_CurrentHead = m_CurrentTail; this->m_Addr = m_CurrentTail; return MemoryRange(oldhead, m_Addr); } } inline FrameHeapBase::State FrameHeapBase::SaveState() { return State(m_CurrentHead, m_CurrentTail); } inline void FrameHeapBase::RestoreState(FrameHeapBase::State state) { NN_TASSERT_(this->m_Addr <= state.m_Head && state.m_Head <= this->m_Addr + this->m_Size); NN_TASSERT_(this->m_Addr <= state.m_Tail && state.m_Tail <= this->m_Addr + this->m_Size); NN_TASSERT_(state.m_Head <= this->m_CurrentHead); NN_TASSERT_(state.m_Tail >= this->m_CurrentTail); this->m_CurrentHead = state.m_Head; this->m_CurrentTail = state.m_Tail; DebugFillMemory(m_CurrentHead, m_CurrentTail - m_CurrentHead, HEAP_FILL_TYPE_FREE); } inline size_t FrameHeapBase::GetAllocatableSize(s32 alignment) const { NN_TASSERT_(alignment != 0); if ( alignment == 0 ) { NN_TPANIC_("invalid argument: alignment == 0"); } if ( alignment < 0 ) { alignment = -alignment; } uptr addr = RoundUp(m_CurrentHead, alignment); if (addr <= m_CurrentTail) { return m_CurrentTail - addr; } else { return 0; } } inline bool FrameHeapBase::HasAddress(const void* addr) const { return m_Addr <= reinterpret_cast(addr) && reinterpret_cast(addr) < (m_Addr+m_Size); } /*! @brief フレームヒープのためのクラステンプレートです。 このクラステンプレートを実体化したクラスでは、 指定したロックポリシーによって、排他制御が行われます。 ロックポリシーは @ref nn::os::LockPolicy クラスの内部で宣言される クラスもしくはクラステンプレートを実体化したクラスを指定することができます。 上述のクラス以外を LockPolicy に指定した場合の動作・互換性は保障されません。 ロックされるのは、ヒープ内の操作に対してのみであり、 ヒープの階層化に対する操作などを行う場合は、適切な排他処理などが別途必要となります。 @tparam LockPolicy ヒープに対する操作を行うときのロックポリシーを指定します。 */ template class FrameHeapTemplate : public FrameHeapBase, private LockPolicy::LockObject { private: typedef FrameHeapBase Base; typedef typename LockPolicy::LockObject LockObject; typedef typename LockPolicy::ScopedLock ScopedLock; public: /*! @brief コンストラクタです。初期化は行いません。 */ FrameHeapTemplate() {} /*! @brief コンストラクタです。フレームヒープの初期化を行います。 @param[in] addr フレームヒープに割り当てるメモリブロックの先頭アドレスを指定します。 @param[in] size フレームヒープに割り当てるメモリブロックのサイズを指定します。 @param[in] option オプションを指定します。未実装です。 */ FrameHeapTemplate(uptr addr, size_t size, bit32 option = 0) { Initialize(addr, size, option); } /*! @brief コンストラクタです。フレームヒープの初期化を行います。 @param[in] block フレームヒープに割り当てるメモリブロックを指定します。 @param[in] option オプションを指定します。 */ template FrameHeapTemplate(const MemoryBlock& block, bit32 option = 0) { Initialize(block.GetAddress(), block.GetSize(), option); } /*! @brief ヒープ内にフレームヒープを作成します。 この関数により作成したヒープは @ref HeapBase::Destroy により解放してください。 FrameHeap自身をaddrとsizeで指定した領域内に作成します。 このため、指定した領域の大きさから sizeof(FrameHeap) の大きさだけ少ない領域をヒープとして割り当てます。 placementによりFrameHeapオブジェクト自身の配置場所を指定します。 placementに HEAP_INFOPLACEMENT_HEAD を指定すると領域の先頭(addr から addr+sizeof(FrameHeap) までの領域)にFrameHeapオブジェクト自身を配置します。 placementに HEAP_INFOPLACEMENT_TAIL を指定すると領域の末尾(addr+size-sizeof(FrameHeap) から addr+size までの領域)にFrameHeapオブジェクト自身を配置します。 @param[in] parent addr を領域内に持つ親のヒープを指定します。 @param[in] addr フレームヒープに割り当てるメモリブロックの先頭アドレスを指定します。 parent で指定されたヒープで確保されたメモリブロックを指定してください。 @param[in] size フレームヒープに割り当てるメモリブロックのサイズを指定します。 @param[in] option オプションを指定します。未実装です。 @param[in] placement 管理領域(FrameHeapオブジェクト)の配置場所を指定します。 @return 作成したヒープを返します。 */ static FrameHeapTemplate* Create(HeapBase* parent, void* addr, size_t size, bit32 option = 0, bit32 placement = HEAP_INFOPLACEMENT_HEAD); /*! @brief フレームヒープの初期化を行います。 @param[in] addr フレームヒープに割り当てるメモリブロックの先頭アドレスを指定します。 @param[in] size フレームヒープに割り当てるメモリブロックのサイズを指定します。 @param[in] option オプションを指定します。 @return 無し。 */ void Initialize(uptr addr, size_t size, bit32 option = 0) { Base::Initialize(addr, size, option); LockObject::Initialize(); } /*! @brief ヒープを破棄します。 @return 無し。 */ void Finalize() { LockObject::Finalize(); Base::Finalize(); } /*! @brief デストラクタです。内部でFinalize()を呼びます。 @return 無し。 */ virtual ~FrameHeapTemplate() {} /*! @brief フレームヒープからメモリを確保します。 @param[in] size 確保するメモリブロックのサイズ(バイト)を指定します。 @param[in] alignment アラインメントを指定します。4,8,16,32,-4,-8,-16,-32のいずれかの値が指定できます。 @return 確保したメモリブロックの先頭アドレスを返します。 正のアライメントを指定すると、ヒープの先頭からメモリを確保し、負のアライメントを指定するとヒープの末尾からメモリを確保します。 */ void* Allocate(size_t size, s32 alignment = DEFAULT_ALIGNMENT) { ScopedLock lk(*this); return Base::Allocate(size, alignment); } /*! @brief フレームヒープから最後に確保したメモリブロックの大きさを変更します。 前方から確保したメモリブロックに対してのみ使用できます。 @param[in] p 最後に確保したメモリブロックの先頭アドレスを指定します。 @param[in] newSize 変更後のサイズ @return 成功すると成功後のサイズを返します。 */ size_t ResizeBlock(void* p, size_t newSize) { ScopedLock lk(*this); return Base::ResizeBlock(p, newSize); } /*! @brief フレームヒープから確保したメモリブロックを全て解放します。 mode に NN_FND_FRAMEHEAP_FREE_HEAD を指定することで前方から確保したメモリブロックを一括して解放します。 mode に NN_FND_FRAMEHEAP_FREE_TAIL を指定することで後方から確保したメモリブロックを一括して解放します。 mode に NN_FND_FRAMEHEAP_FREE_ALL を指定することで、ヒープから確保した全てのメモリブロックを一括して解放します。MEM_FRMHEAP_FREE_HEAD と MEM_FRMHEAP_FREE_TAIL を同時に指定した場合と同じです。 @param[in] mode メモリブロックの解放方法 @return 無し。 */ void Free(int mode = NN_FND_FRAMEHEAP_FREE_ALL) { ScopedLock lk(*this); Base::FreeAll(mode); } /*! @brief 他の種類のヒープと整合性を取るために定義されており、この関数を呼び出すことはできません。 @param[in] p 解放するメモリブロックの先頭アドレスを指定します。 @return 無し。 */ virtual void FreeV(void*) { NN_TASSERT_(0); } class ScopedFrame; /*! @brief フレームヒープの状態を保存します。 @return フレームヒープの状態を返します。 */ State SaveState() { ScopedLock lk(*this); return Base::SaveState(); } /*! @brief フレームヒープの状態を復元します。 @param[in] state フレームヒープの状態を指定します。 @return 無し。 */ void RestoreState(State state) { ScopedLock lk(*this); Base::RestoreState(state); } /*! @brief フレームヒープのサイズを、確保したメモリブロックを包含する最小のサイズに変更します。 mode に NN_FND_FRAMEHEAP_ADJUST_TAIL(もしくは、正の値)を指定することで、後方の空き領域を捨てる形でフレームヒープ領域の先端位置を変更します。 後方にメモリブロックを確保している場合は失敗します。 mode に NN_FND_FRAMEHEAP_ADJUST_HEAD(もしくは、負の値)を指定することで、前方の空き領域を捨てる形でフレームヒープ領域の末端位置を変更します。 前方にメモリブロックを確保している場合は失敗します。 @param[in] mode メモリブロックを縮小する方向を指定します。 @return ヒープが縮小されることにより空いたメモリ領域の範囲を返します。 */ MemoryRange Adjust(int mode = NN_FND_FRAMEHEAP_ADJUST_TAIL) { ScopedLock lk(*this); return Base::Adjust(mode); } /*! @brief フレームヒープから確保可能なメモリブロックのサイズを取得します。 @param[in] alignment アラインメントを指定します。 @return フレームヒープから確保可能なメモリブロックのサイズを返します。 */ size_t GetAllocatableSize(s32 alignment = DEFAULT_ALIGNMENT) const { ScopedLock lk(*this); return Base::GetAllocatableSize(alignment); } /*! @brief フレームヒープのメモリ領域の開始アドレスを取得します。 @return フレームヒープのメモリ領域の開始アドレスをを返します。 */ virtual void* GetStartAddress() const { ScopedLock lk(*this); return Base::GetStartAddress(); } /*! @brief フレームヒープに割り当てられているメモリサイズを取得します。 @return フレームヒープに割り当てられているメモリサイズを返します。 */ virtual size_t GetTotalSize() const { ScopedLock lk(*this); return Base::GetTotalSize(); } /*! @brief 指定したアドレスがヒープに含まれているか調べます。 @param[in] addr 調べたいアドレスを指定します。 @return ヒープに含まれていれば true を返し、含まれていなければ false を返します。 */ virtual bool HasAddress(const void* addr) const { ScopedLock lk(*this); return Base::HasAddress(addr); } /*! @brief ヒープ内部の情報を表示します。(デバッグ用) @return 無し。 */ virtual void Dump() const { Base::Dump(); }; // FrameHeap のアロケータはサポートしない //class Allocator; private: uptr m_Addr; size_t m_Size; uptr m_CurrentHead; // 空き領域の最初のアドレス uptr m_CurrentTail; // 空き領域の最後のアドレス+4 bit32 m_Option; }; /*! @brief スコープごとにフレームヒープの状態を保存するクラスです。 */ template class FrameHeapTemplate::ScopedFrame { public: /*! @brief コンストラクタです。指定されたフレームヒープの内部状態を保存します。 @param heap フレームヒープを指定します。フレームヒープの状態を保存します。 */ explicit ScopedFrame(FrameHeapTemplate& heap) : m_State(heap), m_Heap(heap) {} /*! @brief デストラクタです。フレームヒープの内部状態を復元します。 */ ~ScopedFrame() { m_Heap.RestoreState(m_State); } private: FrameHeapTemplate::State m_State; FrameHeapTemplate& m_Heap; }; /*! @brief ロック操作をしないスレッドアンセーフなフレームヒープを表す型です。 */ typedef FrameHeapTemplate FrameHeap; /*! @brief ヒープごとに用意されるクリティカルセクションで保護された、スレッドセーフなフレームヒープを表す型です。 */ typedef FrameHeapTemplate > ThreadSafeFrameHeap; template FrameHeapTemplate* FrameHeapTemplate::Create(HeapBase* parent, void* addr, size_t size, bit32 option, bit32 placement) { FrameHeapTemplate* heap; if ( parent->FindHeap(addr) != parent ) return 0; if ( placement == HEAP_INFOPLACEMENT_HEAD ) { heap = new (addr) FrameHeapTemplate(reinterpret_cast(addr)+sizeof(FrameHeapTemplate), static_cast(size - sizeof(FrameHeapTemplate)), option); } else if ( placement == HEAP_INFOPLACEMENT_TAIL ) { void* placeaddr = reinterpret_cast(reinterpret_cast(addr)+static_cast(size - sizeof(FrameHeapTemplate))); heap = new (placeaddr) FrameHeapTemplate(reinterpret_cast(addr), static_cast(size - sizeof(FrameHeapTemplate)), option); } else { return 0; } heap->SetParent(parent); return heap; } }} #endif #endif