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