using System;
using System.Collections.Generic;
namespace Ryujinx.Memory.Range
{
///
/// A range list that assumes ranges are non-overlapping, with list items that can be split in two to avoid overlaps.
///
/// Type of the range.
class NonOverlappingRangeList : RangeList where T : INonOverlappingRange
{
///
/// Finds a list of regions that cover the desired (address, size) range.
/// If this range starts or ends in the middle of an existing region, it is split and only the relevant part is added.
/// If there is no matching region, or there is a gap, then new regions are created with the factory.
/// Regions are added to the list in address ascending order.
///
/// List to add found regions to
/// Start address of the search region
/// Size of the search region
/// Factory for creating new ranges
public void GetOrAddRegions(List list, ulong address, ulong size, Func factory)
{
// (regarding the specific case this generalized function is used for)
// A new region may be split into multiple parts if multiple virtual regions have mapped to it.
// For instance, while a virtual mapping could cover 0-2 in physical space, the space 0-1 may have already been reserved...
// So we need to return both the split 0-1 and 1-2 ranges.
var results = new T[1];
int count = FindOverlapsNonOverlapping(address, size, ref results);
if (count == 0)
{
// The region is fully unmapped. Create and add it to the range list.
T region = factory(address, size);
list.Add(region);
Add(region);
}
else
{
ulong lastAddress = address;
ulong endAddress = address + size;
for (int i = 0; i < count; i++)
{
T region = results[i];
if (count == 1 && region.Address == address && region.Size == size)
{
// Exact match, no splitting required.
list.Add(region);
return;
}
if (lastAddress < region.Address)
{
// There is a gap between this region and the last. We need to fill it.
T fillRegion = factory(lastAddress, region.Address - lastAddress);
list.Add(fillRegion);
Add(fillRegion);
}
if (region.Address < address)
{
// Split the region around our base address and take the high half.
region = Split(region, address);
}
if (region.EndAddress > address + size)
{
// Split the region around our end address and take the low half.
Split(region, address + size);
}
list.Add(region);
lastAddress = region.EndAddress;
}
if (lastAddress < endAddress)
{
// There is a gap between this region and the end. We need to fill it.
T fillRegion = factory(lastAddress, endAddress - lastAddress);
list.Add(fillRegion);
Add(fillRegion);
}
}
}
///
/// Splits a region around a target point and updates the region list.
/// The original region's size is modified, but its address stays the same.
/// A new region starting from the split address is added to the region list and returned.
///
/// The region to split
/// The address to split with
/// The new region (high part)
private T Split(T region, ulong splitAddress)
{
Remove(region);
T newRegion = (T)region.Split(splitAddress);
Add(region);
Add(newRegion);
return newRegion;
}
}
}