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