RETURNS [given: YggDIDMap.Run] ~ {
Find a logical run marked free in the in-memory bitmap. May be smaller than "size", but is never smaller than "min" if anything is allocated. If given.pages is 0 then no pages allocated. This flavor of allocation is for the shadow bitmap. To make the allocation stable, StableAlloc must be called with the run.
ENABLE UNWIND => NULL;
Restrict run length to LAST[CARDINAL], to maximize runs per run-table page
pos: YggFile.PageNumber;
realMaxPage: YggFile.PageNumber;
IF segment.first.freePages < size THEN RETURN WITH ERROR YggFile.Error[volumeFull];
IF size = 0 THEN RETURN[YggDIDMap.NullRun];
realMaxPage ← [MIN[segment.first.numberOfPages, maxPage]];
pos ← IF first = 0 THEN segment.first.rover ELSE first;
IF pos < minPage OR pos > maxPage THEN pos ← minPage;
min ← MAX[ MIN[min, size], 1 ]; -- otherwise the algorithm doesn't work!
Loop probing for a free page at "min" intervals
THROUGH [0 .. (realMaxPage-minPage+min-1)/min]
DO
IF pos >= realMaxPage THEN pos ← [minPage];
IF
NOT IsUsedInternal[segment, pos]
THEN {
Expand around "pos" to see if there's a run of size at least "min"
start, end: YggFile.PageNumber ← pos;
given.pages ← 1;
WHILE start > 0
AND given.pages < size
DO
IF IsUsedInternal[segment, [start-1]] THEN EXIT;
start ← [start-1];
given.pages ← given.pages + 1;
ENDLOOP;
WHILE end < segment.first.numberOfPages-1
AND given.pages < size
DO
IF IsUsedInternal[segment, [end+1]] THEN EXIT;
end ← [end+1];
given.pages ← given.pages + 1;
ENDLOOP;
IF given.pages >= min THEN { given.firstPage ← start; EXIT };
};
pos ← [pos+min];
REPEAT FINISHED => RETURN[YggDIDMap.NullRun];
ENDLOOP;
doFreeOrAllocate[segment.first.vmAddressForShadowAllocMap, [segmentId: segment.first.segmentId, segmentPage: given.firstPage, firstPage: 0, pages: given.pages, leader: FALSE], TRUE];
IF first = 0 THEN segment.first.rover ← [given.firstPage];
};