1 /*---------------------------------------------------------------------------*
2   Project:  NintendoWare
3   File:     ut_FrameHeap.cpp
4 
5   Copyright (C)2009-2011 Nintendo/HAL Laboratory, Inc.  All rights reserved.
6 
7   These coded instructions, statements, and computer programs contain proprietary
8   information of Nintendo and/or its licensed developers and are protected by
9   national and international copyright laws. They may not be disclosed to third
10   parties or copied or duplicated in any form, in whole or in part, without the
11   prior written consent of Nintendo.
12 
13   The content herein is highly confidential and should be handled accordingly.
14 
15   $Revision: $
16  *---------------------------------------------------------------------------*/
17 
18 #include "precompiled.h"
19 
20 #include <nw/ut/ut_FrameHeap.h>
21 #include <nw/ut/ut_Inlines.h>  // for GetOffsetFromPtr(), etc...
22 #include <new>
23 
24 namespace nw {
25 namespace ut {
26 
27 /*---------------------------------------------------------------------------*
28   Name:         Create
29 
30   Description:  フレームヒープを作成します。
31 
32   Arguments:    startAddress: ヒープ領域の先頭アドレス。
33                 size:         ヒープ領域のサイズ。
34                 optFlag:      オプションフラグ。
35                               MEM_HEAP_OPT_0_CLEAR    - メモリ確保時の0クリアフラグ
36                               MEM_HEAP_OPT_DEBUG_FILL - デバッグフィルフラグ
37 
38   Returns:      関数が成功した場合、作成されたフレームヒープのハンドルが返ります。
39                 関数が失敗すると、MEM_INVALID_HEAP_HANDLE が返ります。
40 
41   Memo:         基本はスレッドセーフではない。
42                 スレッドセーフにする場合、ヒープの属性を指定する引数を追加するようにするか、
43                 あるいは、属性をセットする関数で制御してもらうか。
44  *---------------------------------------------------------------------------*/
Create(void * startAddress,u32 size,u16 optFlag)45 FrameHeap* FrameHeap::Create( void* startAddress, u32 size, u16 optFlag )
46 {
47     void* endAddress;
48 
49     NW_ASSERT( startAddress != NULL );
50 
51     endAddress   = RoundDown( AddOffsetToPtr( startAddress, size ), MIN_ALIGNMENT );
52     startAddress = RoundUp( startAddress, MIN_ALIGNMENT );
53 
54     // start/end がおかしかったり、FrameHeap サイズに足りない場合は NULL を返す
55     if ( ComparePtr( startAddress, endAddress ) > 0
56         || GetOffsetFromPtr( startAddress, endAddress ) < sizeof(FrameHeap) )
57     {
58         return NULL;
59     }
60 
61     // フレームヒープ初期化
62     {
63         FrameHeap* pHeap = new( startAddress ) FrameHeap;
64 
65         pHeap->Initialize(
66             FRMHEAP_SIGNATURE,
67             AddOffsetToPtr( pHeap, sizeof(FrameHeap) ), // heapStart
68             endAddress,                                 // heapEnd
69             optFlag );
70 
71         pHeap->mHeadAllocator = pHeap->GetHeapStart();
72         pHeap->mTailAllocator = pHeap->GetHeapEnd();
73         pHeap->mpState = NULL;
74 
75         return pHeap;
76     }
77 }
78 
79 /*!--------------------------------------------------------------------------*
80   Name:        Destroy
81 
82   @brief       フレームヒープを破棄します。
83 
84   @return      破棄したヒープが占めていた領域へのポインタを返します。
85  *---------------------------------------------------------------------------*/
Destroy()86 void* FrameHeap::Destroy()
87 {
88     NW_ASSERT( IsValid() );
89 
90     Finalize();
91     return this;
92 }
93 
94 /*!--------------------------------------------------------------------------*
95   Name:        Alloc
96 
97   @brief       フレームヒープからメモリブロックを確保します。
98                メモリブロックのアライメントを指定できます。
99                アライメント値を負の値で指定すると、ヒープの空き領域を後方から探します。
100 
101   @param[in]   size       確保するメモリブロックのサイズ(バイト単位)。
102   @param[in]   alignment  確保するメモリブロックのアライメント。
103                           ±4, ±8, ±16, ±32, ±64, ±128, ... の値が指定できます。
104 
105   @return      メモリブロックの確保が成功した場合、確保したメモリブロックへの
106                ポインタが返ります。
107                失敗した場合、NULLが返ります。
108  *---------------------------------------------------------------------------*/
Alloc(u32 size,int alignment)109 void* FrameHeap::Alloc( u32 size, int alignment )
110 {
111     void* memory = NULL;
112 
113     NW_ASSERT( IsValid() );
114 
115     // アライメントのチェック
116     NW_ASSERT( alignment % MIN_ALIGNMENT == 0 );
117     NW_ASSERT( (Abs(alignment) & (Abs(alignment) - 1)) == 0 );
118     NW_ASSERT( MIN_ALIGNMENT <= Abs(alignment) );
119 
120     // size 補正
121     if ( size == 0 )
122     {
123         size = 1;
124     }
125     size = RoundUp( size, MIN_ALIGNMENT );
126 
127     LockHeap();
128 
129     if ( alignment >= 0 )   // ヒープ先頭から確保
130     {
131         memory = AllocFromHead( size, alignment );
132     }
133     else                    // ヒープ後ろから確保
134     {
135         memory = AllocFromTail( size, -alignment );
136     }
137 
138     UnlockHeap();
139 
140     return memory;
141 }
142 
143 /*!--------------------------------------------------------------------------*
144   Name:        AllocFromHead
145 
146   @brief       ヒープの先頭からメモリブロックを確保します。
147                アラインメントの指定があります。
148 
149   @param[in]   size     確保するメモリブロックのサイズ。
150   @param[in]   alignment   アライメント値。
151 
152   @return      メモリブロックの確保が成功した場合、確保したメモリブロックへの
153                ポインタが返ります。
154                失敗した場合、NULLが返ります。
155  *---------------------------------------------------------------------------*/
AllocFromHead(u32 size,int alignment)156 void* FrameHeap::AllocFromHead( u32 size, int alignment )
157 {
158     void* newBlock = RoundUp( mHeadAllocator, (u32)alignment );
159     void* endAddress = AddOffsetToPtr( newBlock, size );
160 
161     if ( GetIntPtr( endAddress ) > GetIntPtr( mTailAllocator ) )
162     {
163         return NULL;
164     }
165 
166     FillAllocMemory(
167         mHeadAllocator,
168         (u32)GetOffsetFromPtr( mHeadAllocator, endAddress )
169     );
170 
171     mHeadAllocator = endAddress;
172 
173     return newBlock;
174 }
175 
176 /*!--------------------------------------------------------------------------*
177   Name:        AllocFromTail
178 
179   @brief       ヒープの末尾からメモリブロックを確保します。
180                アラインメントの指定があります。
181 
182   @param[in]   size        確保するメモリブロックのサイズ。
183   @param[in]   alignment   アライメント値。
184 
185   @return      メモリブロックの確保が成功した場合、確保したメモリブロックへの
186                ポインタが返ります。
187                失敗した場合、NULLが返ります。
188  *---------------------------------------------------------------------------*/
AllocFromTail(u32 size,int alignment)189 void* FrameHeap::AllocFromTail( u32 size, int alignment )
190 {
191     void* newBlock = RoundDown(
192         AddOffsetToPtr( mTailAllocator, -static_cast<s32>(size) ), static_cast<u32>(alignment) );
193 
194     if ( GetIntPtr( newBlock ) < GetIntPtr( mHeadAllocator ) )
195     {
196         return NULL;
197     }
198 
199     FillAllocMemory(
200         newBlock,
201         (u32)GetOffsetFromPtr( newBlock, mTailAllocator )
202     );
203 
204     mTailAllocator = newBlock;
205 
206     return newBlock;
207 }
208 
209 /*---------------------------------------------------------------------------*
210   Name:         ResizeForMBlock
211 
212   Description:  フレームヒープから確保されたメモリブロックのサイズを変更します。
213 
214                 サイズを変更するメモリブロックは、ヒープの空き領域の前方から確保された
215                 末尾のメモリブロックである必要があります。
216 
217   Arguments:    memBlock:  サイズを変更するメモリブロックへのポインタ。
218                 newSize:   新しく割り当てるサイズ(バイト単位)。
219                            4未満の値を指定された場合は、4が指定されたものとして処理します。
220 
221   Returns:      関数が成功した場合、変更されたメモリブロックのサイズを返します(バイト単位)。
222                 関数が失敗した場合、0 が返ります。
223  *---------------------------------------------------------------------------*/
ResizeForMBlock(void * memBlock,u32 newSize)224 u32 FrameHeap::ResizeForMBlock( void* memBlock, u32 newSize )
225 {
226     NW_ASSERT( IsValid() );
227 
228     // 最低限、最小アライメントの境界にあるかチェック
229     NW_ASSERT( memBlock == RoundDown( memBlock, MIN_ALIGNMENT ) );
230 
231     // メモリブロックは前方に存在すること
232     NW_ASSERT( ComparePtr( mHeapStart, memBlock ) <= 0
233         && ComparePtr( mHeadAllocator, memBlock ) > 0 );
234 
235     // 状態保存がメモリブロックの後方に無いこと
236     NW_ASSERT( mpState == NULL || ComparePtr( mpState, memBlock ) < 0 );
237 
238     // newSize == 0 は認めない。
239     // 0 だと、memBlock が指すメモリブロックが存在しなくなるため。
240     if ( newSize == 0 )
241     {
242         newSize = 1;
243     }
244     newSize = RoundUp( newSize, MIN_ALIGNMENT );
245 
246     LockHeap();
247 
248     {
249         const int oldSize = GetOffsetFromPtr( memBlock, mHeadAllocator );
250         void* endAddress = AddOffsetToPtr( memBlock, newSize );
251 
252         // 同じ場合はなにもしない
253         if ( newSize != oldSize )
254         {
255             // 拡大するとき
256             if ( newSize > static_cast<u32>(oldSize) )
257             {
258                 // サイズが足りない
259                 if ( ComparePtr( endAddress, mTailAllocator ) > 0 )
260                 {
261                     newSize = 0;
262                 }
263                 else
264                 {
265                     FillAllocMemory( mHeadAllocator, newSize - oldSize );
266                 }
267             }
268             // 縮小するとき
269             else
270             {
271                 FillFreeMemory( endAddress, oldSize - newSize );
272             }
273             mHeadAllocator = endAddress;
274         }
275     }
276 
277     UnlockHeap();
278 
279     return newSize;
280 }
281 
282 /*!--------------------------------------------------------------------------*
283   Name:        GetAllocatableSize
284 
285   @brief       フレームヒープ内の割り当て可能な最大サイズを取得します。
286                メモリブロックのアライメントを指定できます。
287 
288   @param[in]   alignment  確保するメモリブロックのアライメント。
289                       ±4, ±8, ±16, ±32, ±64, ±128, ... の値が指定できます。
290 
291   @return      フレームヒープ内の割り当て可能な最大サイズを返します(バイト単位)。
292  *---------------------------------------------------------------------------*/
GetAllocatableSize(int alignment)293 u32 FrameHeap::GetAllocatableSize( int alignment )
294 {
295     NW_ASSERT( IsValid() );
296 
297     NW_ASSERT( alignment % MIN_ALIGNMENT == 0 );
298     NW_ASSERT( (Abs( alignment ) & (Abs( alignment ) - 1)) == 0 );
299     NW_ASSERT( MIN_ALIGNMENT <= Abs( alignment ) );
300 
301     alignment = Abs( alignment );
302 
303     {
304         u32 retVal;
305 #ifdef NW_UT_HEAP_MULTITHREADED
306         bool enabled = OS_DisableIrq();
307 #endif
308         const void* block = RoundUp( mHeadAllocator, (u32)alignment );
309 
310         if ( GetIntPtr( block ) > GetIntPtr( mTailAllocator ) )
311         {
312             retVal = 0;
313         }
314         else
315         {
316             retVal = (u32)GetOffsetFromPtr( block, mTailAllocator );
317         }
318 #ifdef NW_UT_HEAP_MULTITHREADED
319         OS_RestoreIrq( enabled );
320 #endif
321         return retVal;
322     }
323 }
324 
325 /*!--------------------------------------------------------------------------*
326   Name:        Free
327 
328   @brief       フレームヒープへメモリブロックを返却します。
329 
330   @param[in]   mode  メモリブロックの返却方法。
331 
332   @return      なし。
333  *---------------------------------------------------------------------------*/
Free(int mode)334 void FrameHeap::Free( int mode )
335 {
336     NW_ASSERT( IsValid() );
337 
338     LockHeap();
339 
340     if ( mode & FREE_HEAD )
341     {
342         FreeHead();
343     }
344 
345     if ( mode & FREE_TAIL )
346     {
347         FreeTail();
348     }
349 
350     UnlockHeap();
351 }
352 
353 /*!--------------------------------------------------------------------------*
354   Name:        FreeHead
355 
356   @brief       ヒープ領域の先頭から確保したメモリブロックを一括して開放します。
357 
358   @return      なし。
359  *---------------------------------------------------------------------------*/
FreeHead()360 void FrameHeap::FreeHead()
361 {
362     NW_ASSERT( ComparePtr( mHeadAllocator, mHeapStart ) >= 0 );
363 
364     FillFreeMemory( mHeapStart, (u32)GetOffsetFromPtr( mHeapStart, mHeadAllocator ) );
365     mHeadAllocator = mHeapStart;
366     mpState = NULL;
367 }
368 
369 /*!--------------------------------------------------------------------------*
370   Name:        FreeTail
371 
372   @brief       ヒープ領域の末尾から確保したメモリブロックを一括して開放します。
373 
374   @return      なし。
375  *---------------------------------------------------------------------------*/
FreeTail()376 void FrameHeap::FreeTail()
377 {
378     NW_ASSERT( ComparePtr( mHeapEnd, mTailAllocator ) >= 0 );
379 
380     FillFreeMemory( mTailAllocator, (u32)GetOffsetFromPtr( mTailAllocator, mHeapEnd ) );
381 
382     // ヒープの割り当て状態の復帰により、解放したメモリブロックが復活してしまわ
383     // ないように、保存状態の交尾割り当てポインタを再設定しておく
384     for ( HeapState* pState = mpState; pState; pState = pState->pPrevState )
385     {
386         pState->tailAllocator = mHeapEnd;
387     }
388     mTailAllocator = mHeapEnd;
389 }
390 
391 /*!--------------------------------------------------------------------------*
392   Name:        RecordState
393 
394   @brief       フレームヒープの現在のメモリ使用状態を記録します。
395                後で記録したメモリ使用状況に戻すことができます。
396                状態を記録するのに20バイト使用します。
397 
398   @param[in]   tagName   状態記録に付けるタグ名。
399 
400   @return      関数が成功した場合、true が返ります。
401                失敗したら、false が返ります。
402  *---------------------------------------------------------------------------*/
RecordState(u32 tagName)403 bool FrameHeap::RecordState( u32 tagName )
404 {
405     bool retVal;
406     NW_ASSERT( IsValid() );
407 
408     LockHeap();
409 
410     {
411         void* oldHeadAllocator = mHeadAllocator;
412         void* stateHeap = AllocFromHead( sizeof(HeapState), MIN_ALIGNMENT );
413 
414         if ( stateHeap == NULL )
415         {
416             retVal = false;
417         }
418         else
419         {
420             HeapState* pState = new( stateHeap ) HeapState;
421             if ( pState == NULL )
422             {
423                 retVal = false;
424             }
425             else
426             {
427                 pState->tagName         = tagName;
428                 pState->headAllocator   = oldHeadAllocator;
429                 pState->tailAllocator   = mTailAllocator;
430                 pState->pPrevState      = mpState;
431 
432                 mpState = pState;
433                 retVal = true;
434             }
435         }
436     }
437 
438     UnlockHeap();
439 
440     return retVal;
441 }
442 
443 /*---------------------------------------------------------------------------*
444   Name:         FreeByState
445 
446   Description:  フレームヒープのメモリブロックを記録された状態に従って返却します。
447                 指定したタグ名を用いてRecordStateForFrmHeap()を呼び出す直前
448                 のメモリの使用状況に戻ります。
449                 タグ名に0を指定すると最後にRecordStateForFrmHeap()を呼び出す
450                 直前の状態になります。
451 
452                 タグ名を指定して返却した場合、それ以後に呼び出された
453                 RecordStateForFrmHeap()によって記録された情報は
454                 無くなります。
455 
456   Arguments:    tagName:  状態記録に付けるタグ名。
457 
458   Returns:      関数が成功した場合、true が返ります。
459                 失敗したら、false が返ります。
460  *---------------------------------------------------------------------------*/
FreeByState(u32 tagName)461 bool FrameHeap::FreeByState( u32 tagName )
462 {
463     bool retVal;
464     NW_ASSERT( IsValid() );
465 
466     LockHeap();
467 
468     {
469         HeapState* pState = mpState;
470 
471         // タグ検索
472         if ( tagName != 0 )
473         {
474             for ( ; pState; pState = pState->pPrevState )
475             {
476                 if ( pState->tagName == tagName )
477                 {
478                     break;
479                 }
480             }
481         }
482 
483         if ( pState == NULL )
484         {
485             retVal = false;
486         }
487         else
488         {
489             void* oldHeadAllocator = mHeadAllocator;
490             void* oldTailAllocator = mTailAllocator;
491 
492             mHeadAllocator = pState->headAllocator;
493             mTailAllocator = pState->tailAllocator;
494             mpState = pState->pPrevState;
495 
496             FillFreeMemory( mHeadAllocator,
497                     (u32)GetOffsetFromPtr( mHeadAllocator, oldHeadAllocator ) );
498             FillFreeMemory( oldTailAllocator,
499                     (u32)GetOffsetFromPtr( oldTailAllocator, mTailAllocator ) );
500 
501             retVal = true;
502         }
503     }
504 
505     UnlockHeap();
506 
507     return retVal;
508 }
509 
510 /*!--------------------------------------------------------------------------*
511   Name:        Adjust
512 
513   @brief       フレームヒープの空き領域をヒープ領域から開放し、その分ヒープ領域を
514                縮小します。
515                ヒープメモリの後ろから確保されたメモリブロックが存在していては
516                いけません。
517 
518   @return      関数が成功した場合、縮小後のフレームヒープのサイズを返します
519                (バイト単位)。
520                失敗した場合、0を返します。
521  *---------------------------------------------------------------------------*/
Adjust()522 u32 FrameHeap::Adjust()
523 {
524     NW_ASSERT( IsValid() );
525 
526     u32 retVal;
527 
528     LockHeap();
529 
530     // 後方から確保されたメモリブロックがある場合は失敗
531     if ( 0 < GetOffsetFromPtr( mTailAllocator, mHeapEnd ) )
532     {
533         retVal = 0;
534     }
535     else
536     {
537         mTailAllocator = mHeadAllocator;
538         mHeapEnd = mHeadAllocator;
539         retVal = (u32)GetOffsetFromPtr( this, mHeapEnd );
540     }
541 
542     UnlockHeap();
543 
544     return retVal;
545 }
546 
547 } // nw::ut
548 } // nw
549