HeapAllocator.cs 4.1 KB

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