1 // Copyright (c) 2012- PPSSPP Project.
3 // This program is free software: you can redistribute it and/or modify
4 // it under the terms of the GNU General Public License as published by
5 // the Free Software Foundation, version 2.0 or later versions.
7 // This program is distributed in the hope that it will be useful,
8 // but WITHOUT ANY WARRANTY; without even the implied warranty of
9 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 // GNU General Public License 2.0 for more details.
12 // A copy of the GPL 2.0 should have been included with the program.
13 // If not, see http://www.gnu.org/licenses/
15 // Official git repository and contact information can be found at
16 // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
20 #include "Common/Log.h"
21 #include "Common/ChunkFile.h"
22 #include "Core/Util/BlockAllocator.h"
23 #include "Core/Reporting.h"
25 // Slow freaking thing but works (eventually) :)
27 BlockAllocator::BlockAllocator(int grain
) : bottom_(NULL
), top_(NULL
), grain_(grain
)
31 BlockAllocator::~BlockAllocator()
36 void BlockAllocator::Init(u32 rangeStart
, u32 rangeSize
)
39 rangeStart_
= rangeStart
;
40 rangeSize_
= rangeSize
;
41 //Initial block, covering everything
42 top_
= new Block(rangeStart_
, rangeSize_
, false, NULL
, NULL
);
46 void BlockAllocator::Shutdown()
48 while (bottom_
!= NULL
)
50 Block
*next
= bottom_
->next
;
57 u32
BlockAllocator::AllocAligned(u32
&size
, u32 sizeGrain
, u32 grain
, bool fromTop
, const char *tag
)
60 if (size
== 0 || size
> rangeSize_
) {
61 ERROR_LOG(HLE
, "Clearly bogus size: %08x - failing allocation", size
);
65 // It could be off step, but the grain should generally be a power of 2.
68 if (sizeGrain
< grain_
)
71 // upalign size to grain
72 size
= (size
+ sizeGrain
- 1) & ~(sizeGrain
- 1);
76 //Allocate from bottom of mem
77 for (Block
*bp
= bottom_
; bp
!= NULL
; bp
= bp
->next
)
80 u32 offset
= b
.start
% grain
;
82 offset
= grain
- offset
;
83 u32 needed
= offset
+ size
;
84 if (b
.taken
== false && b
.size
>= needed
)
90 return b
.start
+ offset
;
94 InsertFreeAfter(&b
, b
.start
+ needed
, b
.size
- needed
);
98 return b
.start
+ offset
;
105 // Allocate from top of mem.
106 for (Block
*bp
= top_
; bp
!= NULL
; bp
= bp
->prev
)
109 u32 offset
= (b
.start
+ b
.size
- size
) % grain
;
110 u32 needed
= offset
+ size
;
111 if (b
.taken
== false && b
.size
>= needed
)
113 if (b
.size
== needed
)
121 InsertFreeBefore(&b
, b
.start
, b
.size
- needed
);
123 b
.start
+= b
.size
- needed
;
134 ERROR_LOG(HLE
, "Block Allocator (%08x-%08x) failed to allocate %i (%08x) bytes of contiguous memory", rangeStart_
, rangeStart_
+ rangeSize_
, size
, size
);
138 u32
BlockAllocator::Alloc(u32
&size
, bool fromTop
, const char *tag
)
140 // We want to make sure it's aligned in case AllocAt() was used.
141 return AllocAligned(size
, grain_
, grain_
, fromTop
, tag
);
144 u32
BlockAllocator::AllocAt(u32 position
, u32 size
, const char *tag
)
147 if (size
> rangeSize_
) {
148 ERROR_LOG(HLE
, "Clearly bogus size: %08x - failing allocation", size
);
152 // Downalign the position so we're allocating full blocks.
153 u32 alignedPosition
= position
;
154 u32 alignedSize
= size
;
155 if (position
& (grain_
- 1)) {
156 DEBUG_LOG(HLE
, "Position %08x does not align to grain.", position
);
157 alignedPosition
&= ~(grain_
- 1);
159 // Since the position was decreased, size must increase.
160 alignedSize
+= alignedPosition
- position
;
163 // Upalign size to grain.
164 alignedSize
= (alignedSize
+ grain_
- 1) & ~(grain_
- 1);
165 // Tell the caller the allocated size from their requested starting position.
166 size
= alignedSize
- (alignedPosition
- position
);
168 Block
*bp
= GetBlockFromAddress(alignedPosition
);
174 ERROR_LOG(HLE
, "Block allocator AllocAt failed, block taken! %08x, %i", position
, size
);
179 // Make sure the block is big enough to split.
180 if (b
.start
+ b
.size
< alignedPosition
+ alignedSize
)
182 ERROR_LOG(HLE
, "Block allocator AllocAt failed, not enough contiguous space %08x, %i", position
, size
);
186 else if (b
.start
== alignedPosition
)
188 InsertFreeAfter(&b
, b
.start
+ alignedSize
, b
.size
- alignedSize
);
190 b
.size
= alignedSize
;
197 int size1
= alignedPosition
- b
.start
;
198 InsertFreeBefore(&b
, b
.start
, size1
);
199 if (b
.start
+ b
.size
> alignedPosition
+ alignedSize
)
200 InsertFreeAfter(&b
, alignedPosition
+ alignedSize
, b
.size
- (alignedSize
+ size1
));
202 b
.start
= alignedPosition
;
203 b
.size
= alignedSize
;
212 ERROR_LOG(HLE
, "Block allocator AllocAt failed :( %08x, %i", position
, size
);
218 ERROR_LOG(HLE
, "Block Allocator (%08x-%08x) failed to allocate %i (%08x) bytes of contiguous memory", rangeStart_
, rangeStart_
+ rangeSize_
, alignedSize
, alignedSize
);
222 void BlockAllocator::MergeFreeBlocks(Block
*fromBlock
)
224 DEBUG_LOG(HLE
, "Merging Blocks");
226 Block
*prev
= fromBlock
->prev
;
227 while (prev
!= NULL
&& prev
->taken
== false)
229 DEBUG_LOG(HLE
, "Block Alloc found adjacent free blocks - merging");
230 prev
->size
+= fromBlock
->size
;
231 if (fromBlock
->next
== NULL
)
234 fromBlock
->next
->prev
= prev
;
235 prev
->next
= fromBlock
->next
;
238 prev
= fromBlock
->prev
;
244 prev
->next
= fromBlock
;
246 Block
*next
= fromBlock
->next
;
247 while (next
!= NULL
&& next
->taken
== false)
249 DEBUG_LOG(HLE
, "Block Alloc found adjacent free blocks - merging");
250 fromBlock
->size
+= next
->size
;
251 fromBlock
->next
= next
->next
;
253 next
= fromBlock
->next
;
259 next
->prev
= fromBlock
;
262 bool BlockAllocator::Free(u32 position
)
264 Block
*b
= GetBlockFromAddress(position
);
273 ERROR_LOG(HLE
, "BlockAllocator : invalid free %08x", position
);
278 bool BlockAllocator::FreeExact(u32 position
)
280 Block
*b
= GetBlockFromAddress(position
);
281 if (b
&& b
->taken
&& b
->start
== position
)
289 ERROR_LOG(HLE
, "BlockAllocator : invalid free %08x", position
);
294 BlockAllocator::Block
*BlockAllocator::InsertFreeBefore(Block
*b
, u32 start
, u32 size
)
296 Block
*inserted
= new Block(start
, size
, false, b
->prev
, b
);
298 if (inserted
->prev
== NULL
)
301 inserted
->prev
->next
= inserted
;
306 BlockAllocator::Block
*BlockAllocator::InsertFreeAfter(Block
*b
, u32 start
, u32 size
)
308 Block
*inserted
= new Block(start
, size
, false, b
, b
->next
);
310 if (inserted
->next
== NULL
)
313 inserted
->next
->prev
= inserted
;
318 void BlockAllocator::CheckBlocks() const
320 for (const Block
*bp
= bottom_
; bp
!= NULL
; bp
= bp
->next
)
322 const Block
&b
= *bp
;
323 if (b
.start
> 0xc0000000) { // probably free'd debug values
324 ERROR_LOG_REPORT(HLE
, "Bogus block in allocator");
326 // Outside the valid range, probably logic bug in allocation.
327 if (b
.start
+ b
.size
> rangeStart_
+ rangeSize_
|| b
.start
< rangeStart_
) {
328 ERROR_LOG_REPORT(HLE
, "Bogus block in allocator");
333 const char *BlockAllocator::GetBlockTag(u32 addr
) const {
334 const Block
*b
= GetBlockFromAddress(addr
);
338 inline BlockAllocator::Block
*BlockAllocator::GetBlockFromAddress(u32 addr
)
340 for (Block
*bp
= bottom_
; bp
!= NULL
; bp
= bp
->next
)
343 if (b
.start
<= addr
&& b
.start
+ b
.size
> addr
)
352 const BlockAllocator::Block
*BlockAllocator::GetBlockFromAddress(u32 addr
) const
354 for (const Block
*bp
= bottom_
; bp
!= NULL
; bp
= bp
->next
)
356 const Block
&b
= *bp
;
357 if (b
.start
<= addr
&& b
.start
+ b
.size
> addr
)
366 u32
BlockAllocator::GetBlockStartFromAddress(u32 addr
) const
368 const Block
*b
= GetBlockFromAddress(addr
);
375 u32
BlockAllocator::GetBlockSizeFromAddress(u32 addr
) const
377 const Block
*b
= GetBlockFromAddress(addr
);
384 void BlockAllocator::ListBlocks() const
386 INFO_LOG(HLE
,"-----------");
387 for (const Block
*bp
= bottom_
; bp
!= NULL
; bp
= bp
->next
)
389 const Block
&b
= *bp
;
390 INFO_LOG(HLE
, "Block: %08x - %08x size %08x taken=%i tag=%s", b
.start
, b
.start
+b
.size
, b
.size
, b
.taken
? 1:0, b
.tag
);
392 INFO_LOG(HLE
,"-----------");
395 u32
BlockAllocator::GetLargestFreeBlockSize() const
397 u32 maxFreeBlock
= 0;
398 for (const Block
*bp
= bottom_
; bp
!= NULL
; bp
= bp
->next
)
400 const Block
&b
= *bp
;
403 if (b
.size
> maxFreeBlock
)
404 maxFreeBlock
= b
.size
;
407 if (maxFreeBlock
& (grain_
- 1))
408 WARN_LOG_REPORT(HLE
, "GetLargestFreeBlockSize: free size %08x does not align to grain %08x.", maxFreeBlock
, grain_
);
412 u32
BlockAllocator::GetTotalFreeBytes() const
415 for (const Block
*bp
= bottom_
; bp
!= NULL
; bp
= bp
->next
)
417 const Block
&b
= *bp
;
423 if (sum
& (grain_
- 1))
424 WARN_LOG_REPORT(HLE
, "GetTotalFreeBytes: free size %08x does not align to grain %08x.", sum
, grain_
);
428 void BlockAllocator::DoState(PointerWrap
&p
)
430 auto s
= p
.Section("BlockAllocator", 1);
436 if (p
.mode
== p
.MODE_READ
)
441 bottom_
= new Block(0, 0, false, NULL
, NULL
);
446 for (int i
= 0; i
< count
; ++i
)
448 top_
->next
= new Block(0, 0, false, top_
, NULL
);
449 top_
->next
->DoState(p
);
455 for (const Block
*bp
= bottom_
; bp
!= NULL
; bp
= bp
->next
)
462 Block
*last
= bottom_
;
463 for (int i
= 0; i
< count
; ++i
)
465 last
->next
->DoState(p
);
475 BlockAllocator::Block::Block(u32 _start
, u32 _size
, bool _taken
, Block
*_prev
, Block
*_next
)
476 : start(_start
), size(_size
), taken(_taken
), prev(_prev
), next(_next
)
478 strcpy(tag
, "(untitled)");
481 void BlockAllocator::Block::SetTag(const char *_tag
)
484 strncpy(tag
, _tag
, 32);
486 strncpy(tag
, "---", 32);
490 void BlockAllocator::Block::DoState(PointerWrap
&p
)
492 auto s
= p
.Section("Block", 1);
499 p
.DoArray(tag
, sizeof(tag
));