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