HeapAllocator.cs
1 using Ryujinx.Common; 2 using System; 3 using System.Collections.Generic; 4 using System.Diagnostics; 5 6 namespace Ryujinx.Horizon 7 { 8 class HeapAllocator 9 { 10 private const ulong InvalidAddress = ulong.MaxValue; 11 12 private readonly struct Range : IComparable<Range> 13 { 14 public ulong Offset { get; } 15 public ulong Size { get; } 16 17 public Range(ulong offset, ulong size) 18 { 19 Offset = offset; 20 Size = size; 21 } 22 23 public int CompareTo(Range other) 24 { 25 return Offset.CompareTo(other.Offset); 26 } 27 } 28 29 private readonly List<Range> _freeRanges; 30 private ulong _currentHeapSize; 31 32 public HeapAllocator() 33 { 34 _freeRanges = new List<Range>(); 35 _currentHeapSize = 0; 36 } 37 38 public ulong Allocate(ulong size, ulong alignment = 1UL) 39 { 40 ulong address = AllocateImpl(size, alignment); 41 42 if (address == InvalidAddress) 43 { 44 ExpandHeap(size + alignment - 1UL); 45 46 address = AllocateImpl(size, alignment); 47 48 Debug.Assert(address != InvalidAddress); 49 } 50 51 return address; 52 } 53 54 private void ExpandHeap(ulong expansionSize) 55 { 56 ulong oldHeapSize = _currentHeapSize; 57 ulong newHeapSize = BitUtils.AlignUp(oldHeapSize + expansionSize, 0x200000UL); 58 59 _currentHeapSize = newHeapSize; 60 61 HorizonStatic.Syscall.SetHeapSize(out ulong heapAddress, newHeapSize).AbortOnFailure(); 62 63 Free(heapAddress + oldHeapSize, newHeapSize - oldHeapSize); 64 } 65 66 private ulong AllocateImpl(ulong size, ulong alignment) 67 { 68 for (int i = 0; i < _freeRanges.Count; i++) 69 { 70 var range = _freeRanges[i]; 71 72 ulong alignedOffset = BitUtils.AlignUp(range.Offset, alignment); 73 ulong sizeDelta = alignedOffset - range.Offset; 74 ulong usableSize = range.Size - sizeDelta; 75 76 if (sizeDelta < range.Size && usableSize >= size) 77 { 78 _freeRanges.RemoveAt(i); 79 80 if (sizeDelta != 0) 81 { 82 InsertFreeRange(range.Offset, sizeDelta); 83 } 84 85 ulong endOffset = range.Offset + range.Size; 86 ulong remainingSize = endOffset - (alignedOffset + size); 87 if (remainingSize != 0) 88 { 89 InsertFreeRange(endOffset - remainingSize, remainingSize); 90 } 91 92 return alignedOffset; 93 } 94 } 95 96 return InvalidAddress; 97 } 98 99 public void Free(ulong offset, ulong size) 100 { 101 InsertFreeRangeComingled(offset, size); 102 } 103 104 private void InsertFreeRange(ulong offset, ulong size) 105 { 106 var range = new Range(offset, size); 107 int index = _freeRanges.BinarySearch(range); 108 if (index < 0) 109 { 110 index = ~index; 111 } 112 113 _freeRanges.Insert(index, range); 114 } 115 116 private void InsertFreeRangeComingled(ulong offset, ulong size) 117 { 118 ulong endOffset = offset + size; 119 var range = new Range(offset, size); 120 int index = _freeRanges.BinarySearch(range); 121 if (index < 0) 122 { 123 index = ~index; 124 } 125 126 if (index < _freeRanges.Count && _freeRanges[index].Offset == endOffset) 127 { 128 endOffset = _freeRanges[index].Offset + _freeRanges[index].Size; 129 _freeRanges.RemoveAt(index); 130 } 131 132 if (index > 0 && _freeRanges[index - 1].Offset + _freeRanges[index - 1].Size == offset) 133 { 134 offset = _freeRanges[index - 1].Offset; 135 _freeRanges.RemoveAt(--index); 136 } 137 138 range = new Range(offset, endOffset - offset); 139 140 _freeRanges.Insert(index, range); 141 } 142 } 143 }