public bool IncrementalCompact(out ulong id, out long size, out long oldStart, out long newStart)
{
//Find the allocation nearest to the zero index. Identify it by checking for the previous allocation requiring a wraparound.
//Start at the beginning of the list since it's marginally more likely to be there than at the end of the list where new allocations get appended.
for (int i = 0; i < allocations.Count; ++i)
{
Allocation previousAllocation;
allocations.TryGetValue(allocations.Values[i].Previous, out previousAllocation);
if (previousAllocation.End > allocations.Values[i].Start)
{
//Found the beginning of the list! This index is the first index.
//Now, scan forward through the allocation links looking for the first gap.
var index = i;
var previousEnd = 0L;
//Note that we stop before wrapping.
for (int iterationIndex = 0; iterationIndex < allocations.Count; ++iterationIndex)
{
searchStartIndex = index; //If the traversal ends, we want to have this index cached so that the next allocation will start at the end of the contiguous block.
Debug.Assert(searchStartIndex >= 0 && searchStartIndex < allocations.Count, "Search start index must be within the allocation set!");
if (allocations.Values[index].Start > previousEnd)
{
//Found a gap.
id = allocations.Keys[index];
size = allocations.Values[index].End - allocations.Values[index].Start;
oldStart = allocations.Values[index].Start;
newStart = previousEnd;
//Actually perform the move.
allocations.Values[index].Start = newStart;
allocations.Values[index].End = newStart + size;
return true;
}
//Haven't found a gap yet. Move to the next.
previousEnd = allocations.Values[index].End;
index = allocations.IndexOf(allocations.Values[index].Next);
}
break;
}
}
id = 0;
size = 0;
oldStart = 0;
newStart = 0;
return false;
//Note: a slightly fancier allocator could 1) track the start and 2) coalesce allocations such that this entire process would become O(1).
//Something to consider if this allocator ever bottlenecks.
}