1 /*---------------------------------------------------------------------------*
2   Project:  Horizon
3   File:     fnd_FrameHeap.h
4 
5   Copyright (C)2009 Nintendo Co., Ltd.  All rights reserved.
6 
7   These coded instructions, statements, and computer programs contain
8   proprietary information of Nintendo of America Inc. and/or Nintendo
9   Company Ltd., and are protected by Federal copyright law.  They may
10   not be disclosed to third parties or copied or duplicated in any form,
11   in whole or in part, without the prior written consent of Nintendo.
12 
13   $Rev: 24310 $
14  *---------------------------------------------------------------------------*/
15 
16 /*! @file
17   @brief    フレームヒープに関するAPIの宣言
18 
19 */
20 
21 #ifndef NN_FND_FND_FRAMEHEAP_H_
22 #define NN_FND_FND_FRAMEHEAP_H_
23 
24 #include <nn/types.h>
25 #include <nn/fnd/fnd_Allocator.h>
26 #include <nn/fnd/fnd_HeapBase.h>
27 #include <nn/fnd/fnd_MemoryRange.h>
28 #include <nn/Assert.h>
29 #include <nn/os.h>
30 
31 #ifdef __cplusplus
32 
33 #define NN_FND_FRAMEHEAP_FREE_HEAD 1
34 #define NN_FND_FRAMEHEAP_FREE_TAIL 2
35 #define NN_FND_FRAMEHEAP_FREE_ALL (NN_FND_FRAMEHEAP_FREE_HEAD | NN_FND_FRAMEHEAP_FREE_TAIL)
36 
37 #define NN_FND_FRAMEHEAP_ADJUST_TAIL 1
38 #define NN_FND_FRAMEHEAP_ADJUST_HEAD -1
39 
40 namespace nn { namespace fnd {
41 
42 /*!
43     @brief    フレームヒープのベースクラスです。
44 
45               このクラスのインスタンスを直接作成することはできません。
46  */
47 class FrameHeapBase : public HeapBase
48 {
49 public:
50 
51     class State;
52 
53 protected:
54 
FrameHeapBase()55     FrameHeapBase() : m_Addr(0) {}
56 
57     FrameHeapBase(uptr addr, size_t size, bit32 option = 0) : m_Addr(0) { Initialize(addr, size, option); }
58 
59     template <class MemoryBlock>
60     FrameHeapBase(const MemoryBlock& block, bit32 option = 0) : m_Addr(0) { Initialize(block.GetAddress(), block.GetSize(), option); }
61 
62     void Initialize(uptr addr, size_t size, bit32 option = 0);
63 
Finalize()64     void Finalize() { m_Addr = 0; }
65 
~FrameHeapBase()66     virtual ~FrameHeapBase() { Finalize(); }
67 
68     void* Allocate(size_t size, s32 alignment);
69 
70     size_t ResizeBlock(void* p, size_t newSize);
71 
72     void FreeAll(int mode);
73 
FreeV(void *)74     virtual void FreeV(void*) {}
75 
76     State SaveState();
77 
78     void RestoreState(State state);
79 
80     MemoryRange Adjust(int mode);
81 
82     size_t GetAllocatableSize(s32 alignment) const;
83 
GetStartAddress()84     virtual void* GetStartAddress() const { return reinterpret_cast<void*>(m_Addr); }
85 
GetTotalSize()86     virtual size_t GetTotalSize() const { return m_Size; }
87 
88     virtual void Dump() const;
89 
90     virtual bool HasAddress(const void* addr) const;
91 
92 private:
93     uptr m_Addr;
94     size_t m_Size;
95     uptr m_CurrentHead;
96     uptr m_CurrentTail;
97 };
98 
99 /*!
100     @brief    フレームヒープの状態を表すクラスです。
101  */
102 class FrameHeapBase::State
103 {
104 public:
105 
106     /*!
107         @brief  コンストラクタです。
108 
109         @param  heap    フレームヒープを指定します。
110      */
State(FrameHeapBase & heap)111     explicit State(FrameHeapBase& heap) : m_Head(heap.m_CurrentHead), m_Tail(heap.m_CurrentTail) {}
112 
113     /*!
114         @brief  コンストラクタです。初期化は行いません。
115      */
State()116     State() : m_Head(0), m_Tail(0) {}
117 
118 private:
119     uptr m_Head;
120     uptr m_Tail;
121 
State(uptr head,uptr tail)122     explicit State(uptr head, uptr tail) : m_Head(head), m_Tail(tail) {}
123 
124     friend class FrameHeapBase;
125 };
126 
Allocate(size_t size,s32 alignment)127 inline void* FrameHeapBase::Allocate(size_t size, s32 alignment)
128 {
129     NN_TASSERT_(size > 0);
130     NN_TASSERT_(alignment != 0);
131     uptr ret;
132 
133     if ( alignment == 0 )
134     {
135         NN_TPANIC_("invalid argument: alignment == 0");
136     }
137 
138     if ( alignment > 0 ) {
139         ret = RoundUp(m_CurrentHead, alignment);
140         uptr current = ret + size;
141 
142         if (current > m_CurrentTail)
143         {
144             return 0;
145         }
146 
147         m_CurrentHead = current;
148     }
149     else
150     {
151         uptr current = ret = RoundDown(m_CurrentTail-size, -alignment);
152 
153         if (current < m_CurrentHead)
154         {
155             return 0;
156         }
157 
158         m_CurrentTail = current;
159     }
160 
161 
162     // フィルオプション
163     DebugFillMemory(ret, size, HEAP_FILL_TYPE_ALLOC);
164     FillMemoryZero(ret, size);
165 
166     return reinterpret_cast<void*>(ret);
167 }
168 
FreeAll(int mode)169 inline void FrameHeapBase::FreeAll(int mode)
170 {
171     NN_TASSERT_(  mode == NN_FND_FRAMEHEAP_FREE_HEAD ||
172                 mode == NN_FND_FRAMEHEAP_FREE_TAIL ||
173                 mode == NN_FND_FRAMEHEAP_FREE_ALL);
174 
175     if ( mode & NN_FND_FRAMEHEAP_FREE_HEAD )
176     {
177         DebugFillMemory(m_Addr, m_CurrentHead - m_Addr, HEAP_FILL_TYPE_FREE);
178         this->m_CurrentHead = this->m_Addr;
179     }
180 
181     if ( mode & NN_FND_FRAMEHEAP_FREE_TAIL )
182     {
183         DebugFillMemory(m_CurrentTail, m_Addr + m_Size - m_CurrentTail, HEAP_FILL_TYPE_FREE);
184         this->m_CurrentTail = this->m_Addr + this->m_Size;
185     }
186 }
187 
ResizeBlock(void * p,size_t newSize)188 inline size_t FrameHeapBase::ResizeBlock(void* p, size_t newSize)
189 {
190     // TODO: debug_fill
191     uptr& addr = reinterpret_cast<uptr&>(p);
192     NN_TASSERT_(m_Addr <= addr && addr < m_CurrentTail);
193     NN_TASSERT_(addr <= m_CurrentHead);
194     uptr current = addr + newSize;
195     if (current < m_CurrentTail)
196     {
197         if ( current < this->m_CurrentHead )
198         {
199             DebugFillMemory(current, this->m_CurrentHead - current, HEAP_FILL_TYPE_FREE);
200         }
201         if ( this->m_CurrentHead < current )
202         {
203             FillMemoryZero(this->m_CurrentHead, current - this->m_CurrentHead);
204         }
205         this->m_CurrentHead = current;
206         return newSize;
207     }
208     else
209     {
210         return 0;
211     }
212 }
213 
Adjust(int mode)214 inline MemoryRange FrameHeapBase::Adjust(int mode)
215 {
216     // TODO: debug_fill
217     if ( mode == 0 )
218     {
219         NN_TPANIC_("invalid argument: mode == 0");
220     }
221 
222     if ( mode > 0 )
223     {
224         if ( m_CurrentTail != m_Addr + m_Size )
225         {
226             NN_TPANIC_("invalid call: tail used");
227         }
228 
229         DebugFillMemory(m_CurrentHead, m_Addr + m_Size - m_CurrentHead, HEAP_FILL_TYPE_FREE);
230 
231         uptr oldtail = m_Addr + m_Size;
232         this->m_Size = m_CurrentHead - m_Addr;
233         this->m_CurrentTail = m_CurrentHead;
234         return MemoryRange(this->m_CurrentTail, oldtail);
235     }
236     else
237     {
238         if ( m_CurrentHead != m_Addr )
239         {
240             NN_TPANIC_("invalid call: head used");
241         }
242 
243         DebugFillMemory(m_Addr, m_CurrentTail - m_Addr, HEAP_FILL_TYPE_FREE);
244 
245         uptr oldhead = m_Addr;
246         this->m_Size = m_Addr + m_Size - m_CurrentTail;
247         this->m_CurrentHead = m_CurrentTail;
248         this->m_Addr = m_CurrentTail;
249         return MemoryRange(oldhead, m_Addr);
250     }
251 }
252 
SaveState()253 inline FrameHeapBase::State FrameHeapBase::SaveState()
254 {
255     return State(m_CurrentHead, m_CurrentTail);
256 }
257 
RestoreState(FrameHeapBase::State state)258 inline void FrameHeapBase::RestoreState(FrameHeapBase::State state)
259 {
260     NN_TASSERT_(this->m_Addr <= state.m_Head && state.m_Head <= this->m_Addr + this->m_Size);
261     NN_TASSERT_(this->m_Addr <= state.m_Tail && state.m_Tail <= this->m_Addr + this->m_Size);
262     NN_TASSERT_(state.m_Head <= this->m_CurrentHead);
263     NN_TASSERT_(state.m_Tail >= this->m_CurrentTail);
264 
265     this->m_CurrentHead = state.m_Head;
266     this->m_CurrentTail = state.m_Tail;
267 
268     DebugFillMemory(m_CurrentHead, m_CurrentTail - m_CurrentHead, HEAP_FILL_TYPE_FREE);
269 }
270 
GetAllocatableSize(s32 alignment)271 inline size_t FrameHeapBase::GetAllocatableSize(s32 alignment) const
272 {
273     NN_TASSERT_(alignment != 0);
274 
275     if ( alignment == 0 )
276     {
277         NN_TPANIC_("invalid argument: alignment == 0");
278     }
279 
280     if ( alignment < 0 )
281     {
282         alignment = -alignment;
283     }
284 
285     uptr addr = RoundUp(m_CurrentHead, alignment);
286     if (addr <= m_CurrentTail)
287     {
288         return m_CurrentTail - addr;
289     }
290     else
291     {
292         return 0;
293     }
294 }
295 
HasAddress(const void * addr)296 inline bool FrameHeapBase::HasAddress(const void* addr) const
297 {
298     return m_Addr <= reinterpret_cast<uptr>(addr)
299         && reinterpret_cast<uptr>(addr) < (m_Addr+m_Size);
300 }
301 
302 /*!
303     @brief    フレームヒープのためのクラステンプレートです。
304 
305               このクラステンプレートを実体化したクラスでは、
306               指定したロックポリシーによって、排他制御が行われます。
307               ロックポリシーは @ref nn::os::LockPolicy クラスの内部で宣言される
308               クラスもしくはクラステンプレートを実体化したクラスを指定することができます。
309               上述のクラス以外を LockPolicy に指定した場合の動作・互換性は保障されません。
310 
311               ロックされるのは、ヒープ内の操作に対してのみであり、
312               ヒープの階層化に対する操作などを行う場合は、適切な排他処理などが別途必要となります。
313 
314     @tparam   LockPolicy ヒープに対する操作を行うときのロックポリシーを指定します。
315 */
316 template <class LockPolicy>
317 class FrameHeapTemplate : public FrameHeapBase, private LockPolicy::LockObject
318 {
319 private:
320     typedef FrameHeapBase Base;
321     typedef typename LockPolicy::LockObject LockObject;
322     typedef typename LockPolicy::ScopedLock ScopedLock;
323 public:
324 
325     /*!
326         @brief      コンストラクタです。初期化は行いません。
327      */
FrameHeapTemplate()328     FrameHeapTemplate() {}
329 
330     /*!
331         @brief      コンストラクタです。フレームヒープの初期化を行います。
332 
333         @param[in]  addr    フレームヒープに割り当てるメモリブロックの先頭アドレスを指定します。
334         @param[in]  size    フレームヒープに割り当てるメモリブロックのサイズを指定します。
335         @param[in]  option  オプションを指定します。未実装です。
336      */
337     FrameHeapTemplate(uptr addr, size_t size, bit32 option = 0) { Initialize(addr, size, option); }
338 
339     /*!
340         @brief      コンストラクタです。フレームヒープの初期化を行います。
341 
342         @param[in]  block   フレームヒープに割り当てるメモリブロックを指定します。
343         @param[in]  option  オプションを指定します。
344      */
345     template <class MemoryBlock>
346     FrameHeapTemplate(const MemoryBlock& block, bit32 option = 0) { Initialize(block.GetAddress(), block.GetSize(), option); }
347 
348     /*!
349         @brief      ヒープ内にフレームヒープを作成します。
350 
351                     この関数により作成したヒープは @ref HeapBase::Destroy により解放してください。
352 
353                     FrameHeap自身をaddrとsizeで指定した領域内に作成します。
354                     このため、指定した領域の大きさから sizeof(FrameHeap) の大きさだけ少ない領域をヒープとして割り当てます。
355 
356                     placementによりFrameHeapオブジェクト自身の配置場所を指定します。
357                     placementに HEAP_INFOPLACEMENT_HEAD を指定すると領域の先頭(addr から addr+sizeof(FrameHeap) までの領域)にFrameHeapオブジェクト自身を配置します。
358                     placementに HEAP_INFOPLACEMENT_TAIL を指定すると領域の末尾(addr+size-sizeof(FrameHeap) から addr+size までの領域)にFrameHeapオブジェクト自身を配置します。
359 
360         @param[in] parent       addr を領域内に持つ親のヒープを指定します。
361         @param[in] addr         フレームヒープに割り当てるメモリブロックの先頭アドレスを指定します。
362                                 parent で指定されたヒープで確保されたメモリブロックを指定してください。
363         @param[in] size         フレームヒープに割り当てるメモリブロックのサイズを指定します。
364         @param[in] option       オプションを指定します。未実装です。
365         @param[in] placement    管理領域(FrameHeapオブジェクト)の配置場所を指定します。
366 
367         @return 作成したヒープを返します。
368     */
369     static FrameHeapTemplate* Create(HeapBase* parent, void* addr, size_t size, bit32 option = 0, bit32 placement = HEAP_INFOPLACEMENT_HEAD);
370 
371     /*!
372         @brief      フレームヒープの初期化を行います。
373 
374         @param[in]  addr    フレームヒープに割り当てるメモリブロックの先頭アドレスを指定します。
375         @param[in]  size    フレームヒープに割り当てるメモリブロックのサイズを指定します。
376         @param[in]  option  オプションを指定します。
377 
378         @return 無し。
379      */
380     void Initialize(uptr addr, size_t size, bit32 option = 0)
381     {
382         Base::Initialize(addr, size, option);
383         LockObject::Initialize();
384     }
385 
386     /*!
387         @brief      ヒープを破棄します。
388 
389         @return     無し。
390      */
Finalize()391     void Finalize()
392     {
393         LockObject::Finalize();
394         Base::Finalize();
395     }
396 
397     /*!
398         @brief      デストラクタです。内部でFinalize()を呼びます。
399 
400         @return     無し。
401      */
~FrameHeapTemplate()402     virtual ~FrameHeapTemplate() {}
403 
404     /*!
405         @brief      フレームヒープからメモリを確保します。
406 
407         @param[in]  size        確保するメモリブロックのサイズ(バイト)を指定します。
408         @param[in]  alignment   アラインメントを指定します。4,8,16,32,-4,-8,-16,-32のいずれかの値が指定できます。
409 
410         @return     確保したメモリブロックの先頭アドレスを返します。
411 
412         正のアライメントを指定すると、ヒープの先頭からメモリを確保し、負のアライメントを指定するとヒープの末尾からメモリを確保します。
413      */
414     void* Allocate(size_t size, s32 alignment = DEFAULT_ALIGNMENT)
415     {
416         ScopedLock lk(*this);
417         return Base::Allocate(size, alignment);
418     }
419 
420     /*!
421         @brief      フレームヒープから最後に確保したメモリブロックの大きさを変更します。
422 
423                     前方から確保したメモリブロックに対してのみ使用できます。
424 
425         @param[in]  p       最後に確保したメモリブロックの先頭アドレスを指定します。
426         @param[in]  newSize 変更後のサイズ
427 
428         @return     成功すると成功後のサイズを返します。
429      */
ResizeBlock(void * p,size_t newSize)430     size_t ResizeBlock(void* p, size_t newSize)
431     {
432         ScopedLock lk(*this);
433         return Base::ResizeBlock(p, newSize);
434     }
435 
436     /*!
437         @brief      フレームヒープから確保したメモリブロックを全て解放します。
438 
439                     mode に NN_FND_FRAMEHEAP_FREE_HEAD を指定することで前方から確保したメモリブロックを一括して解放します。
440 
441                     mode に NN_FND_FRAMEHEAP_FREE_TAIL を指定することで後方から確保したメモリブロックを一括して解放します。
442 
443                     mode に NN_FND_FRAMEHEAP_FREE_ALL を指定することで、ヒープから確保した全てのメモリブロックを一括して解放します。MEM_FRMHEAP_FREE_HEAD と MEM_FRMHEAP_FREE_TAIL を同時に指定した場合と同じです。
444 
445         @param[in]  mode    メモリブロックの解放方法
446 
447         @return     無し。
448      */
449     void Free(int mode = NN_FND_FRAMEHEAP_FREE_ALL)
450     {
451         ScopedLock lk(*this);
452         Base::FreeAll(mode);
453     }
454 
455     /*!
456         @brief      他の種類のヒープと整合性を取るために定義されており、この関数を呼び出すことはできません。
457 
458         @param[in]  p   解放するメモリブロックの先頭アドレスを指定します。
459 
460         @return     無し。
461      */
FreeV(void *)462     virtual void FreeV(void*) { NN_TASSERT_(0); }
463 
464     class ScopedFrame;
465 
466     /*!
467         @brief      フレームヒープの状態を保存します。
468 
469         @return     フレームヒープの状態を返します。
470      */
SaveState()471     State SaveState()
472     {
473         ScopedLock lk(*this);
474         return Base::SaveState();
475     }
476 
477     /*!
478         @brief      フレームヒープの状態を復元します。
479 
480         @param[in]  state   フレームヒープの状態を指定します。
481 
482         @return     無し。
483      */
RestoreState(State state)484     void RestoreState(State state)
485     {
486         ScopedLock lk(*this);
487         Base::RestoreState(state);
488     }
489 
490     /*!
491         @brief      フレームヒープのサイズを、確保したメモリブロックを包含する最小のサイズに変更します。
492 
493                     mode に NN_FND_FRAMEHEAP_ADJUST_TAIL(もしくは、正の値)を指定することで、後方の空き領域を捨てる形でフレームヒープ領域の先端位置を変更します。
494                     後方にメモリブロックを確保している場合は失敗します。
495 
496                     mode に NN_FND_FRAMEHEAP_ADJUST_HEAD(もしくは、負の値)を指定することで、前方の空き領域を捨てる形でフレームヒープ領域の末端位置を変更します。
497                     前方にメモリブロックを確保している場合は失敗します。
498 
499         @param[in]  mode    メモリブロックを縮小する方向を指定します。
500 
501         @return     ヒープが縮小されることにより空いたメモリ領域の範囲を返します。
502      */
503     MemoryRange Adjust(int mode = NN_FND_FRAMEHEAP_ADJUST_TAIL)
504     {
505         ScopedLock lk(*this);
506         return Base::Adjust(mode);
507     }
508 
509     /*!
510         @brief      フレームヒープから確保可能なメモリブロックのサイズを取得します。
511 
512         @param[in]  alignment   アラインメントを指定します。
513 
514         @return     フレームヒープから確保可能なメモリブロックのサイズを返します。
515      */
516     size_t GetAllocatableSize(s32 alignment = DEFAULT_ALIGNMENT) const
517     {
518         ScopedLock lk(*this);
519         return Base::GetAllocatableSize(alignment);
520     }
521 
522     /*!
523         @brief フレームヒープのメモリ領域の開始アドレスを取得します。
524 
525         @return フレームヒープのメモリ領域の開始アドレスをを返します。
526      */
GetStartAddress()527     virtual void* GetStartAddress() const
528     {
529         ScopedLock lk(*this);
530         return Base::GetStartAddress();
531     }
532 
533     /*!
534         @brief フレームヒープに割り当てられているメモリサイズを取得します。
535 
536         @return フレームヒープに割り当てられているメモリサイズを返します。
537      */
GetTotalSize()538     virtual size_t GetTotalSize() const
539     {
540         ScopedLock lk(*this);
541         return Base::GetTotalSize();
542     }
543 
544     /*!
545         @brief  指定したアドレスがヒープに含まれているか調べます。
546 
547         @param[in]  addr    調べたいアドレスを指定します。
548 
549         @return ヒープに含まれていれば true を返し、含まれていなければ false を返します。
550     */
HasAddress(const void * addr)551     virtual bool HasAddress(const void* addr) const
552     {
553         ScopedLock lk(*this);
554         return Base::HasAddress(addr);
555     }
556 
557     /*!
558         @brief ヒープ内部の情報を表示します。(デバッグ用)
559 
560         @return 無し。
561      */
Dump()562     virtual void Dump() const { Base::Dump(); };
563 
564     // FrameHeap のアロケータはサポートしない
565     //class Allocator;
566 
567 private:
568     uptr m_Addr;
569     size_t m_Size;
570     uptr m_CurrentHead; // 空き領域の最初のアドレス
571     uptr m_CurrentTail; // 空き領域の最後のアドレス+4
572     bit32 m_Option;
573 };
574 
575 /*!
576     @brief    スコープごとにフレームヒープの状態を保存するクラスです。
577  */
578 template <class LockPolicy>
579 class FrameHeapTemplate<LockPolicy>::ScopedFrame
580 {
581 public:
582     /*!
583         @brief  コンストラクタです。指定されたフレームヒープの内部状態を保存します。
584 
585         @param  heap    フレームヒープを指定します。フレームヒープの状態を保存します。
586      */
ScopedFrame(FrameHeapTemplate<LockPolicy> & heap)587     explicit ScopedFrame(FrameHeapTemplate<LockPolicy>& heap) : m_State(heap), m_Heap(heap) {}
588     /*!
589         @brief  デストラクタです。フレームヒープの内部状態を復元します。
590      */
~ScopedFrame()591     ~ScopedFrame() { m_Heap.RestoreState(m_State); }
592 
593 private:
594     FrameHeapTemplate<LockPolicy>::State m_State;
595     FrameHeapTemplate<LockPolicy>& m_Heap;
596 };
597 
598 /*!
599     @brief ロック操作をしないスレッドアンセーフなフレームヒープを表す型です。
600 */
601 typedef FrameHeapTemplate<nn::os::LockPolicy::NoLock> FrameHeap;
602 
603 /*!
604     @brief ヒープごとに用意されるクリティカルセクションで保護された、スレッドセーフなフレームヒープを表す型です。
605 */
606 typedef FrameHeapTemplate<nn::os::LockPolicy::Object<nn::os::CriticalSection> > ThreadSafeFrameHeap;
607 
608 template <class LockPolicy>
Create(HeapBase * parent,void * addr,size_t size,bit32 option,bit32 placement)609 FrameHeapTemplate<LockPolicy>* FrameHeapTemplate<LockPolicy>::Create(HeapBase* parent, void* addr, size_t size, bit32 option, bit32 placement)
610 {
611     FrameHeapTemplate* heap;
612 
613     if ( parent->FindHeap(addr) != parent ) return 0;
614 
615     if ( placement == HEAP_INFOPLACEMENT_HEAD )
616     {
617         heap = new (addr) FrameHeapTemplate(reinterpret_cast<uptr>(addr)+sizeof(FrameHeapTemplate), static_cast<size_t>(size - sizeof(FrameHeapTemplate)), option);
618     }
619     else if ( placement == HEAP_INFOPLACEMENT_TAIL )
620     {
621         void* placeaddr = reinterpret_cast<void*>(reinterpret_cast<uptr>(addr)+static_cast<size_t>(size - sizeof(FrameHeapTemplate)));
622         heap = new (placeaddr) FrameHeapTemplate(reinterpret_cast<uptr>(addr), static_cast<size_t>(size - sizeof(FrameHeapTemplate)), option);
623     }
624     else
625     {
626         return 0;
627     }
628 
629     heap->SetParent(parent);
630     return heap;
631 }
632 
633 }}
634 
635 #endif
636 
637 #endif
638