/*---------------------------------------------------------------------------* Project: Horizon File: fnd_UnitHeap.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: 15614 $ *---------------------------------------------------------------------------*/ /*! @file @brief ユニットヒープに関するAPIの宣言 */ #ifndef NN_FND_FND_UNIT_HEAP_H_ #define NN_FND_FND_UNIT_HEAP_H_ #include #include #include #include #include #include #ifdef __cplusplus namespace nn { namespace fnd { /*! @brief ユニットヒープのベースクラスです。 このクラスのインスタンスを直接作成することはできません。 */ class UnitHeapBase : public HeapBase { public: /*! @brief 指定したユニットサイズとアラインメントのブロックを指定数を管理できるだけのヒープメモリのサイズを取得します。 @param[in] unit ユニットのサイズを指定します。 @param[in] numUnit 確保したいブロックの数。 @param[in] alignment ユニットのアラインメント。 */ static size_t GetRequiredHeapSize(size_t unit, size_t numUnit, s32 alignment = DEFAULT_ALIGNMENT); protected: UnitHeapBase() : m_FreeNode(0) {} UnitHeapBase(size_t unit, uptr addr, size_t size, s32 alignment = DEFAULT_ALIGNMENT, bit32 option = 0) : m_FreeNode(0) { Initialize(unit, addr, size, alignment, option); } template explicit UnitHeapBase(size_t unit, const MemoryBlock& block, s32 alignment = DEFAULT_ALIGNMENT, bit32 option = 0) : m_FreeNode(0) { Initialize(unit, block.GetAddress(), block.GetSize(), alignment, option); } void Initialize(size_t unit, uptr addr, size_t size, s32 alignment = DEFAULT_ALIGNMENT, bit32 option = 0); void Invalidate(); void Finalize(); virtual ~UnitHeapBase(); void* Allocate(); void Free(void* p); virtual void FreeV(void* p); size_t GetUnitSize() const { return m_Unit; } size_t GetAllocatableCount() const { return m_Size / m_Unit - m_Count; } 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: struct Node { Node* next; }; size_t m_Unit; uptr m_Addr; size_t m_Size; Node* m_FreeNode; s32 m_Alignment; size_t m_Count; }; inline void UnitHeapBase::Finalize() { if (m_FreeNode == 0) { return; } NN_TASSERT_(m_Count == 0); this->m_FreeNode = 0; } inline UnitHeapBase::~UnitHeapBase() { Finalize(); } inline void UnitHeapBase::Invalidate() { this->m_FreeNode = 0; } inline void* UnitHeapBase::Allocate() { void* ret = reinterpret_cast(m_FreeNode); if (ret) { this->m_FreeNode = m_FreeNode->next; ++this->m_Count; // フィルオプション DebugFillMemory(reinterpret_cast(ret), m_Unit, HEAP_FILL_TYPE_ALLOC); FillMemoryZero(reinterpret_cast(ret), m_Unit); } return ret; } inline bool UnitHeapBase::HasAddress(const void* addr) const { return m_Addr <= reinterpret_cast(addr) && reinterpret_cast(addr) < (m_Addr+m_Size); } inline void UnitHeapBase::Free(void* p) { NN_TASSERT_(HasAddress(p)); DebugFillMemory(reinterpret_cast(p), m_Unit, HEAP_FILL_TYPE_FREE); reinterpret_cast(p)->next = m_FreeNode; this->m_FreeNode = reinterpret_cast(p); --this->m_Count; } inline void UnitHeapBase::FreeV(void* p) { Free(p); } /*! @brief ユニットヒープのためのクラステンプレートです。 このクラステンプレートを実体化したクラスでは、 指定したロックポリシーによって、排他制御が行われます。 ロックポリシーは @ref nn::os::LockPolicy クラスの内部で宣言される クラスもしくはクラステンプレートを実体化したクラスを指定することができます。 上述のクラス以外を LockPolicy に指定した場合の動作・互換性は保障されません。 ロックされるのは、ヒープ内の操作に対してのみであり、 ヒープの階層化に対する操作などを行う場合は、適切な排他処理などが別途必要となります。 @tparam LockPolicy ヒープに対する操作を行うときのロックポリシーを指定します。 */ template class UnitHeapTemplate : public UnitHeapBase, private LockPolicy::LockObject { private: typedef UnitHeapBase Base; typedef typename LockPolicy::LockObject LockObject; typedef typename LockPolicy::ScopedLock ScopedLock; public: /*! @brief 指定したユニットサイズとアラインメントのブロックを指定数を管理できるだけのヒープメモリのサイズを取得します。 @param[in] unit ユニットのサイズを指定します。 @param[in] numUnit 確保したいブロックの数。 @param[in] alignment ユニットのアラインメント。 */ static size_t GetRequiredHeapSize(size_t unit, size_t numUnit, s32 alignment = DEFAULT_ALIGNMENT) { return Base::GetRequiredHeapSize(unit, numUnit, alignment); } /*! @brief コンストラクタです。初期化は行いません。 */ UnitHeapTemplate() {} /*! @brief コンストラクタです。指定したパラメータを用いてユニットヒープの初期化を行います。 @param[in] unit ユニットのサイズを指定します。 @param[in] addr ユニットヒープで使うメモリの先頭アドレスを指定します。 @param[in] size ユニットヒープで使うメモリのサイズを指定します。 @param[in] alignment ユニットのアラインメントを指定します。 @param[in] option オプションを指定します。未実装です。 */ UnitHeapTemplate(size_t unit, uptr addr, size_t size, s32 alignment = DEFAULT_ALIGNMENT, bit32 option = 0) { Initialize(unit, addr, size, alignment, option); } /*! @brief コンストラクタです。指定したパラメータを用いてユニットヒープの初期化を行います。 @param[in] unit ユニットのサイズを指定します。 @param[in] block フレームヒープに割り当てるメモリブロックを指定します。 @param[in] alignment ユニットのアラインメントを指定します。 @param[in] option オプションを指定します。未実装です。 */ template explicit UnitHeapTemplate(size_t unit, const MemoryBlock& block, s32 alignment = DEFAULT_ALIGNMENT, bit32 option = 0) { Initialize(unit, block.GetAddress(), block.GetSize(), alignment, option); } /*! @brief ヒープ内にユニットヒープを作成します。 この関数で作成したヒープは @ref HeapBase::Destroy により破壊してください。 ExpHeap自身をaddrとsizeで指定した領域内に作成します。 このため、指定した領域の大きさから sizeof(ExpHeap) の大きさだけ少ない領域をヒープとして割り当てます。 placementによりExpHeapオブジェクト自身の配置場所を指定します。 placementに HEAP_INFOPLACEMENT_HEAD を指定すると領域の先頭(addr から addr+sizeof(ExpHeap) までの領域)にExpHeapオブジェクト自身を配置します。 placementに HEAP_INFOPLACEMENT_TAIL を指定すると領域の末尾(addr+size-sizeof(ExpHeap) から addr+size までの領域)にExpHeapオブジェクト自身を配置します。 @param[in] parent addr を領域内に持つ親のヒープを指定します。 @param[in] unit ユニットのサイズを指定します。 @param[in] addr ユニットヒープで使うメモリの先頭アドレスを指定します。 @param[in] size ユニットヒープで使うメモリのサイズを指定します。 @param[in] alignment ユニットのアラインメントを指定します。 @param[in] option オプションを指定します。未実装です。 @param[in] placement 管理領域(UnitHeapオブジェクト)の配置場所を指定します。 */ static UnitHeapTemplate* Create(HeapBase* parent, size_t unit, void* addr, size_t size, s32 alignment = DEFAULT_ALIGNMENT, bit32 option = 0, bit32 placement = HEAP_INFOPLACEMENT_HEAD); /*! @brief 指定したパラメータを用いてユニットヒープの初期化を行います。 初期化無しのコンストラクタを使ってオブジェクトを生成した場合に用います。 @param[in] unit ユニットのサイズを指定します。 @param[in] addr ユニットヒープで使うメモリの先頭アドレスを指定します。このアドレスは alignment パラメータの値でアラインメントされている必要があります。 @param[in] size ユニットヒープで使うメモリのサイズを指定します。 @param[in] alignment ユニットのアラインメントを指定します。 @param[in] option オプションを指定します。未実装です。 */ void Initialize(size_t unit, uptr addr, size_t size, s32 alignment = DEFAULT_ALIGNMENT, bit32 option = 0) { Base::Initialize(unit, addr, size, alignment, option); LockObject::Initialize(); } /*! @brief ヒープオブジェクトを無効化します。 この関数を呼んだ後は @ref Finalize 以外の操作をヒープに対して行うことができなくなります。 */ void Invalidate() { Base::Invalidate(); } /*! @brief ヒープオブジェクトの終了処理をします。 この関数はデストラクタの中で暗黙的に呼ばれるため、明示的に呼ぶ必要はありません。 ヒープの中に未解放のブロックが存在した場合、アプリケーションは強制終了します。 意図して未解放のままこの関数を呼ぶ際は、その前に、明示的に @ref Invalidate 関数を呼んでください。 */ void Finalize() { LockObject::Finalize(); Base::Finalize(); } /*! @brief デストラクタです。 破棄時の注意は @ref Finalize を参照してください。 */ virtual ~UnitHeapTemplate() {} /*! @brief ヒープからユニットに基づいてメモリを確保します。 @return 確保に成功した場合は、メモリへのポインタを返します。 確保に失敗した場合は、0 を返します。 */ void* Allocate() { ScopedLock lk(*this); return Base::Allocate(); } /*! @brief このヒープから確保したメモリを解放します。 @param[in] p メモリへのポインタを指定します。 */ void Free(void* p) { ScopedLock lk(*this); Base::Free(p); } /*! @brief このヒープから確保したメモリを解放します。 仮想メンバ関数であるため、HeapBaseから解放できます。 @param[in] p メモリへのポインタを指定します。 */ virtual void FreeV(void* p) { Free(p); } /*! @brief このユニットヒープのユニットサイズを取得します。 @return ユニットサイズを返します。 */ size_t GetUnitSize() const { return Base::GetUnitSize(); } /*! @brief 現在このヒープから確保できるユニットの数を取得します。 @return 確保できるユニットの数を返します。 */ size_t GetAllocatableCount() const { ScopedLock lk(*this); return Base::GetAllocatableCount(); } /*! @brief ユニットヒープのメモリ領域の開始アドレスを取得します。 @return ユニットヒープのメモリ領域の開始アドレスをを返します。 */ virtual void* GetStartAddress() const { return Base::GetStartAddress(); } /*! @brief ユニットヒープに割り当てられているメモリサイズを取得します。 @return ユニットヒープに割り当てられているメモリサイズを返します。 */ virtual size_t GetTotalSize() const { return Base::GetTotalSize(); } /*! @brief ヒープ内部の情報を表示します。(デバッグ用) @return 無し。 */ virtual void Dump() const { ScopedLock lk(*this); Base::Dump(); } /*! @brief 指定したアドレスがヒープに含まれているか調べます。 @param[in] addr 調べたいアドレスを指定します。 @return ヒープに含まれていれば true を返し、含まれていなければ false を返します。 */ virtual bool HasAddress(const void* addr) const { return Base::HasAddress(addr); } class Allocator; }; /*! @brief ユニットヒープ用のアロケータクラスです。 */ template class UnitHeapTemplate::Allocator : public IAllocator { public: /*! @brief ユニットヒープを指定してアロケータを初期化するコンストラクタです。 @param[in] ユニットヒープを指定します。 */ Allocator(UnitHeapTemplate& heap) : m_Heap(&heap) {} /*! @brief 初期化をしないコンストラクタです。 */ Allocator() : m_Heap(0) {} /*! @brief 初期化されていないアロケータを初期化します。 @param[in] ユニットヒープを指定します。 */ void Initialize(UnitHeapTemplate& heap) { m_Heap = &heap; } /*! @brief アロケータにセットされているユニットヒープを返します。 @return アロケータにセットされているユニットヒープ */ UnitHeapTemplate* GetHeap() { return m_Heap; } /*! @brief アロケータにセットされているユニットヒープを返します。 @return アロケータにセットされているユニットヒープ */ const UnitHeapTemplate* GetHeap() const { return m_Heap; } /*! @brief 指定したサイズとアラインメントでメモリ領域を確保します。 @param[in] size 確保するメモリのサイズ @param[in] alignment 確保するメモリのアラインメント @return 確保したメモリ領域の先頭へのポインタ */ virtual void* Allocate(size_t size, s32 alignment); /*! @brief メモリ領域を解放します。 @param[in] p 確保されているメモリ領域の先頭へのポインタ */ virtual void Free(void* p); private: UnitHeapTemplate* m_Heap; }; template inline void* UnitHeapTemplate::Allocator::Allocate(size_t size, s32 alignment) { if (size == m_Heap->m_Unit && alignment >= 0 && m_Heap->m_Alignment % alignment == 0) { return m_Heap->Allocate(); } else { return 0; } } template inline void UnitHeapTemplate::Allocator::Free(void* p) { m_Heap->Free(p); } /*! @brief ロック操作をしないスレッドアンセーフなユニットヒープを表す型です。 */ typedef UnitHeapTemplate UnitHeap; /*! @brief ヒープごとに用意されるクリティカルセクションで保護された、スレッドセーフなユニットヒープを表す型です。 */ typedef UnitHeapTemplate > ThreadSafeUnitHeap; template UnitHeapTemplate* UnitHeapTemplate::Create(HeapBase* parent, size_t unit, void* addr, size_t size, s32 alignment, bit32 option, bit32 placement) { UnitHeapTemplate* heap; if ( parent->FindHeap(addr) != parent ) return 0; if ( placement == HEAP_INFOPLACEMENT_HEAD ) { heap = new (addr) UnitHeapTemplate(unit, reinterpret_cast(addr)+sizeof(UnitHeapTemplate), static_cast(size - sizeof(UnitHeapTemplate)), alignment, option); } else if ( placement == HEAP_INFOPLACEMENT_TAIL ) { void* placeaddr = reinterpret_cast(reinterpret_cast(addr)+static_cast(size - sizeof(UnitHeapTemplate))); heap = new (placeaddr) UnitHeapTemplate(unit, reinterpret_cast(addr), static_cast(size - sizeof(UnitHeapTemplate)), alignment, option); } else { return 0; } heap->SetParent(parent); return heap; } }} #endif #endif