using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; namespace Ryujinx.Memory.Range { public class RangeItem(TValue value) where TValue : IRange { public RangeItem Next; public RangeItem Previous; public readonly ulong Address = value.Address; public readonly ulong EndAddress = value.Address + value.Size; public readonly TValue Value = value; public readonly List QuickAccessAddresses = []; [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool OverlapsWith(ulong address, ulong endAddress) { return Address < endAddress && address < EndAddress; } } class AddressEqualityComparer : IEqualityComparer { public bool Equals(ulong u1, ulong u2) { return u1 == u2; } public int GetHashCode(ulong value) => (int)(value << 5); public static readonly AddressEqualityComparer Comparer = new(); } public unsafe abstract class RangeListBase : IEnumerable where T : IRange { private const int BackingInitialSize = 1024; protected RangeItem[] Items; protected readonly int BackingGrowthSize; public int Count { get; protected set; } /// /// Creates a new range list. /// /// The initial size of the backing array protected RangeListBase(int backingInitialSize = BackingInitialSize) { BackingGrowthSize = backingInitialSize; Items = new RangeItem[backingInitialSize]; } public abstract void Add(T item); /// /// Updates an item's end address on the list. Address must be the same. /// /// The item to be updated /// True if the item was located and updated, false otherwise protected abstract bool Update(T item); /// /// Updates an item's end address on the list. Address must be the same. /// /// The RangeItem to be updated /// True if the item was located and updated, false otherwise protected abstract bool Update(RangeItem item); public abstract bool Remove(T item); public abstract void RemoveRange(RangeItem startItem, RangeItem endItem); public abstract RangeItem FindOverlap(ulong address, ulong size); public abstract RangeItem FindOverlapFast(ulong address, ulong size); /// /// Performs binary search on the internal list of items. /// /// Address to find /// List index of the item, or complement index of nearest item with lower value on the list [MethodImpl(MethodImplOptions.AggressiveInlining)] protected int BinarySearch(ulong address) { int left = 0; int right = Count - 1; while (left <= right) { int range = right - left; int middle = left + (range >> 1); ref RangeItem item = ref Items[middle]; if (item.Address == address) { return middle; } if (address < item.Address) { right = middle - 1; } else { left = middle + 1; } } return ~left; } /// /// Performs binary search for items overlapping a given memory range. /// /// Start address of the range /// End address of the range /// List index of the item, or complement index of nearest item with lower value on the list [MethodImpl(MethodImplOptions.AggressiveInlining)] protected int BinarySearch(ulong address, ulong endAddress) { int left = 0; int right = Count - 1; while (left <= right) { int range = right - left; int middle = left + (range >> 1); ref RangeItem item = ref Items[middle]; if (item.OverlapsWith(address, endAddress)) { return middle; } if (address < item.Address) { right = middle - 1; } else { left = middle + 1; } } return ~left; } /// /// Performs binary search for items overlapping a given memory range. /// /// Start address of the range /// End address of the range /// List index of the item, or complement index of nearest item with lower value on the list [MethodImpl(MethodImplOptions.AggressiveInlining)] protected int BinarySearchLeftEdge(ulong address, ulong endAddress) { if (Count == 0) return ~0; int left = 0; int right = Count - 1; while (left <= right) { int range = right - left; int middle = left + (range >> 1); ref RangeItem item = ref Items[middle]; bool match = item.OverlapsWith(address, endAddress); if (range == 0) { if (match) return middle; else if (address < item.Address) return ~(right); else return ~(right + 1); } if (match) { right = middle; } else if (address < item.Address) { right = middle - 1; } else { left = middle + 1; } } return ~left; } /// /// Performs binary search for items overlapping a given memory range. /// /// Start address of the range /// End address of the range /// List index of the item, or complement index of nearest item with lower value on the list [MethodImpl(MethodImplOptions.AggressiveInlining)] protected int BinarySearchRightEdge(ulong address, ulong endAddress) { if (Count == 0) return ~0; int left = 0; int right = Count - 1; while (left <= right) { int range = right - left; int middle = right - (range >> 1); ref RangeItem item = ref Items[middle]; bool match = item.OverlapsWith(address, endAddress); if (range == 0) { if (match) return middle; else if (endAddress > item.EndAddress) return ~(left + 1); else return ~(left); } if (match) { left = middle; } else if (address < item.Address) { right = middle - 1; } else { left = middle + 1; } } return ~left; } /// /// Performs binary search for items overlapping a given memory range. /// /// Start address of the range /// End address of the range /// Range information (inclusive, exclusive) of items that overlaps, or complement index of nearest item with lower value on the list [MethodImpl(MethodImplOptions.AggressiveInlining)] protected (int, int) BinarySearchEdges(ulong address, ulong endAddress) { if (Count == 0) return (~0, ~0); if (Count == 1) { ref RangeItem item = ref Items[0]; if (item.OverlapsWith(address, endAddress)) { return (0, 1); } if (address < item.Address) { return (~0, ~0); } else { return (~1, ~1); } } int left = 0; int right = Count - 1; int leftEdge = -1; int rightEdgeMatch = -1; int rightEdgeNoMatch = -1; while (left <= right) { int range = right - left; int middle = left + (range >> 1); ref RangeItem item = ref Items[middle]; bool match = item.OverlapsWith(address, endAddress); if (range == 0) { if (match) { leftEdge = middle; break; } else if (address < item.Address) { return (~right, ~right); } else { return (~(right + 1), ~(right + 1)); } } if (match) { right = middle; if (rightEdgeMatch == -1) rightEdgeMatch = middle; } else if (address < item.Address) { right = middle - 1; rightEdgeNoMatch = middle; } else { left = middle + 1; } } if (left > right) { return (~left, ~left); } if (rightEdgeMatch == -1) { return (leftEdge, leftEdge + 1); } left = rightEdgeMatch; right = rightEdgeNoMatch > 0 ? rightEdgeNoMatch : Count - 1; while (left <= right) { int range = right - left; int middle = right - (range >> 1); ref RangeItem item = ref Items[middle]; bool match = item.OverlapsWith(address, endAddress); if (range == 0) { if (match) return (leftEdge, middle + 1); else return (leftEdge, middle); } if (match) { left = middle; } else if (address < item.Address) { right = middle - 1; } else { left = middle + 1; } } return (leftEdge, right + 1); } public abstract IEnumerator GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } }