-- HeapImpl.mesa (last edited by: McJones on: February 12, 1981 6:48 PM)

DIRECTORY
Environment USING [maxPagesInMDS, PageCount, wordsPerPage],
Heap USING [Create, CreateMDS, defaultSwapUnit, ErrorType, NWords],
Inline USING [LowHalf],
MiscPrograms USING [],
PilotSwitches USING [switches--.u--],
Runtime USING [GetCaller],
Space USING [Create, CreateUniformSwapUnits, Delete, Error,
GetAttributes, GetHandle, Handle, InsufficientSpace, LongPointer, Map, mds,
nullHandle, PageCount, PageFromLongPointer, virtualMemory],
SpecialHeap USING [],
SpecialSpace USING [MakeResident, MakeSwappable],
SystemInternal USING [Unimplemented],
Zone USING [AddSegment, Base, Create, FreeNode, GetAttributes,
GetSegmentAttributes, Handle, MakeNode, minimumNodeSize, nullSegment,
RemoveSegment, SegmentHandle, SetChecking, Status],
ZoneInternal USING [NodeHeader, ZoneHeader];

HeapImpl: MONITOR
IMPORTS
Heap, Inline, PilotSwitches, Runtime, Space, SpecialSpace, SystemInternal, Zone
EXPORTS Heap, MiscPrograms, SpecialHeap =
BEGIN

--
-- Types

NWords: TYPE = Heap.NWords;

UncountedZoneRep: TYPE = LONG POINTER TO UncountedZoneObject;
UncountedZoneObject: TYPE = MACHINE DEPENDENT RECORD [
procs (0:0..31): LONG POINTER TO Procs,
data (2:0..31): Handle];
Procs: TYPE = MACHINE DEPENDENT RECORD [
Make (0): PROCEDURE [UNCOUNTED ZONE, NWords] RETURNS [LONG POINTER],
Free (1): PROCEDURE [UNCOUNTED ZONE, LONG POINTER]
-- could add other generic Heap operations here
];
Handle: TYPE = LONG POINTER TO Data;

MDSZoneRep: TYPE = POINTER TO MDSZoneObject;
MDSZoneObject: TYPE = MACHINE DEPENDENT RECORD [
procs (0:0..15): POINTER TO MDSProcs,
data (1:0..15): MDSHandle];
MDSProcs: TYPE = MACHINE DEPENDENT RECORD [
Make (0): PROCEDURE [MDSZone, NWords] RETURNS [POINTER],
Free (1): PROCEDURE [MDSZone, POINTER]
-- could add other generic (MDS) Heap operations here
];
MDSHandle: TYPE = POINTER TO Data;

Data: TYPE = RECORD [
seal: INTEGER,
parent: Space.Handle,
base: Zone.Base, -- = Space.LongPointer[parent]
zH: Zone.Handle,
resident: BOOLEAN ← FALSE,
ownerChecking,
checking: BOOLEAN,
increment,
swapUnit: Environment.PageCount,
threshold,
largeNodeThreshold: NWords,
largeNodes: LONG POINTER TO LargeNodeHeader ← NIL,
vp: SELECT OVERLAID * FROM
normal => [uzo: UncountedZoneObject],
mds => [mzo: MDSZoneObject],
ENDCASE];

LargeNodeHeader: TYPE = MACHINE DEPENDENT RECORD [
next (0): LONG POINTER TO LargeNodeHeader,
nodeHeader (2): inuse ZoneInternal.NodeHeader,
node (3): ARRAY [0..0) OF RECORD [UNSPECIFIED]];

--
-- Constants

currentSeal: INTEGER = 12345;
defaultSwapUnit: Space.PageCount = Heap.defaultSwapUnit;
pMDS: LONG POINTER = Space.LongPointer[Space.mds];
nodeOverhead: CARDINAL = SIZE[inuse ZoneInternal.NodeHeader];
zoneOverhead: CARDINAL = SIZE[ZoneInternal.ZoneHeader]+nodeOverhead;
minimumNodeSize: PUBLIC NWords = Zone.minimumNodeSize;
initial: Environment.PageCount ← 2;
initialMDS: Environment.PageCount ← 2;
standardProcs: Procs ← [Make: MakeNode, Free: FreeNode];
standardMDSProcs: MDSProcs ← [Make: MakeMDSNode, Free: FreeMDSNode];
hyperSpace: Space.Handle; -- initialized in InitializeHeap
systemZone: PUBLIC UNCOUNTED ZONE; -- initialized in InitializeHeap
systemMDSZone: PUBLIC MDSZone ← Heap.CreateMDS[initial: initialMDS];

--
-- Initialization (MiscPrograms)

InitializeHeap: PUBLIC PROCEDURE =
BEGIN
-- Because UtilityPilot currently only allows one level of spaces, with parent equal to
-- virtualMemory or mds, we can’t support general "hyper" heaps. We make a special
-- case of systemZone, by identifying it with systemMDSZone.
IF PilotSwitches.switches.u=down THEN -- UtilityPilot
BEGIN
uz: UncountedZoneRep = systemMDSZone.NEW[UncountedZoneObject ← [
procs: @standardProcs,
data: LOOPHOLE[systemMDSZone, MDSZoneRep].data]];
hyperSpace ← Space.nullHandle; -- Heap.Create unimplemented in UtilityPilot
systemZone ← LOOPHOLE[uz];
END
ELSE
BEGIN
hyperSpace ← Space.Create[
parent: Space.virtualMemory,
size: Environment.maxPagesInMDS];
systemZone ← Heap.Create[initial: initial]
END;
END;

--
-- Heap implementation

Create: PUBLIC PROCEDURE [
initial: Environment.PageCount, parent: Space.Handle,
increment, swapUnit: Environment.PageCount,
threshold, largeNodeThreshold: NWords, ownerChecking, checking: BOOLEAN]
RETURNS [UNCOUNTED ZONE] =
BEGIN
h: Handle;
IF PilotSwitches.switches.u=down THEN ERROR SystemInternal.Unimplemented;
IF parent=Space.nullHandle THEN parent ← hyperSpace
ELSE IF Space.GetAttributes[parent].size>256
THEN ERROR Error[unsuitableParent];
h ← CreateHeap[
initial, parent, increment, swapUnit, threshold, largeNodeThreshold,
ownerChecking, checking];
h.uzo ← [procs: @standardProcs, data: h];
RETURN[LOOPHOLE[@h.uzo]]
END;

CreateMDS: PUBLIC PROCEDURE [
initial: Environment.PageCount,
increment, swapUnit: Environment.PageCount,
threshold, largeNodeThreshold: NWords, ownerChecking, checking: BOOLEAN]
RETURNS [MDSZone] =
BEGIN
h: MDSHandle = Narrow[CreateHeap[
initial, Space.mds, increment, swapUnit, threshold, largeNodeThreshold,
ownerChecking, checking]];
h.mzo ← [procs: @standardMDSProcs, data: h];
RETURN[LOOPHOLE[@h.uzo]]
END;

CreateHeap: PROCEDURE [
initial: Environment.PageCount, parent: Space.Handle,
increment, swapUnit: Environment.PageCount,
threshold, largeNodeThreshold: NWords, ownerChecking, checking: BOOLEAN]
RETURNS [Handle] =
BEGIN
zH: Zone.Handle;
status: Zone.Status;
heap: Zone.Base RELATIVE POINTER TO Data;
base: Zone.Base;
seg: LONG POINTER;
IF initial=0 THEN initial ← 1; -- since not yet ready to extend
base ← Space.LongPointer[parent];
seg ← MakeSpace[parent: parent, pages: initial, swapUnit: swapUnit, resident: FALSE
! Space.InsufficientSpace => GOTO InsufficientSpace];
[zH: zH, s: status] ← Zone.Create[
storage: seg,
length: initial*Environment.wordsPerPage,
zoneBase: base,
threshold: threshold,
checking: checking];
IF status~=okay THEN ERROR Error[otherError];
[heap, status] ← Zone.MakeNode[zH: zH, n: SIZE[Data]];
IF status~=okay THEN ERROR Error[otherError];
base[heap] ← [
seal: currentSeal,
parent: parent,
base: base,
zH: zH,
ownerChecking: ownerChecking,
checking: checking,
increment: increment,
swapUnit: swapUnit,
threshold: threshold,
largeNodeThreshold: largeNodeThreshold,
vp: NULL];
RETURN[@base[heap]]
EXITS InsufficientSpace => ERROR Error[insufficientSpace]
END;

Delete: PUBLIC PROCEDURE [z: UNCOUNTED ZONE, checkEmpty: BOOLEAN] =
BEGIN OPEN rep: LOOPHOLE[z, UncountedZoneRep];
DeleteHeap[rep.data, checkEmpty];
END;

DeleteMDS: PUBLIC PROCEDURE [z: MDSZone, checkEmpty: BOOLEAN] =
BEGIN OPEN rep: LOOPHOLE[z, MDSZoneRep];
DeleteHeap[rep.data, checkEmpty];
END;

DeleteHeap: PROCEDURE [h: Handle, checkEmpty: BOOLEAN] =
BEGIN
DeleteSpace: PROCEDURE [sH: Space.Handle, isLargeNode: BOOLEAN, segH: Zone.SegmentHandle] =
BEGIN
IF checkEmpty AND (isLargeNode OR
segH~=Zone.nullSegment AND Zone.RemoveSegment[h.zH, segH].s~=okay) THEN
ERROR Error[invalidHeap];
Space.Delete[sH];
END;
EnumerateSpaces[h, DeleteSpace];
END;

MakeNode: PUBLIC PROCEDURE [z: UNCOUNTED ZONE, n: NWords] RETURNS [p: LONG POINTER] =
BEGIN
h: Handle = LOOPHOLE[z, UncountedZoneRep].data;
r: Zone.Base RELATIVE POINTER;
status: Zone.Status;
IF h.ownerChecking THEN n ← n+1;
IF n>=h.largeNodeThreshold THEN
p ← MakeLargeNode[h, n]
ELSE
DO
[node: r, s: status] ← Zone.MakeNode[zH: h.zH, n: n];
SELECT status FROM
okay => BEGIN p ← @h.base[r]; EXIT END;
noRoomInZone => ExpandHeap[h, PagesForNewSegment[n]];
ENDCASE => ERROR Error[invalidHeap];
ENDLOOP;
IF h.ownerChecking THEN BEGIN p↑ ← Runtime.GetCaller[]; p ← p+1 END;
END;

FreeNode: PUBLIC PROCEDURE [z: UNCOUNTED ZONE, p: LONG POINTER] =
BEGIN
h: Handle = LOOPHOLE[z, UncountedZoneRep].data;
IF h.ownerChecking THEN p ← p-1;
IF LOOPHOLE[p-SIZE[inuse ZoneInternal.NodeHeader],
LONG POINTER TO ZoneInternal.NodeHeader].length-nodeOverhead<h.largeNodeThreshold THEN
SELECT Zone.FreeNode[zH: h.zH, p: p] FROM -- surely regular node
okay => NULL;
invalidNode => ERROR Error[invalidNode];
ENDCASE => ERROR Error[invalidHeap]
ELSE FreeLargeOrRegularNode[h, p] -- possibly large node
END;

MakeMDSNode: PUBLIC PROCEDURE [z: MDSZone, n: NWords] RETURNS [p: POINTER] =
BEGIN
h: Handle = LOOPHOLE[z, MDSZoneRep].data;
status: Zone.Status;
IF h.ownerChecking THEN n ← n+1;
-- since we can’t delete space in UtilityPilot, treat large as small
IF n>=h.largeNodeThreshold AND PilotSwitches.switches.u=up THEN
p ← Narrow[MakeLargeNode[h, n]]
ELSE
DO
[node: LOOPHOLE[p, Zone.Base RELATIVE POINTER], s: status] ←
Zone.MakeNode[zH: h.zH, n: n];
SELECT status FROM
okay => EXIT;
noRoomInZone => ExpandHeap[h, PagesForNewSegment[n]];
ENDCASE => ERROR Error[invalidHeap]
ENDLOOP;
IF h.ownerChecking THEN BEGIN p↑ ← Runtime.GetCaller[]; p ← p+1 END;
END;

FreeMDSNode: PUBLIC PROCEDURE [z: MDSZone, p: POINTER] =
BEGIN
h: Handle = LOOPHOLE[z, MDSZoneRep].data;
IF h.ownerChecking THEN p ← p-1;
IF LOOPHOLE[p-SIZE[inuse ZoneInternal.NodeHeader],
POINTER TO ZoneInternal.NodeHeader].length-nodeOverhead<h.largeNodeThreshold THEN
SELECT Zone.FreeNode[zH: h.zH, p: p] FROM -- surely regular node
okay => NULL;
invalidNode => ERROR Error[invalidNode];
ENDCASE => ERROR Error[invalidHeap]
ELSE FreeLargeOrRegularNode[h, p] -- possibly large node
END;

GetAttributes: PUBLIC PROCEDURE [z: UNCOUNTED ZONE] RETURNS [
parent: Space.Handle,
heapPages, largeNodePages, increment, swapUnit: Environment.PageCount,
threshold, largeNodeThreshold: NWords, ownerChecking, checking: BOOLEAN] =
BEGIN OPEN data: LOOPHOLE[z, UncountedZoneRep].data;
[
parent: parent,
heapPages: heapPages,
largeNodePages: largeNodePages,
increment: increment,
swapUnit: swapUnit,
threshold: threshold,
largeNodeThreshold: largeNodeThreshold,
ownerChecking: ownerChecking] ← GetAttributesHeap[@data];
END;

GetAttributesMDS: PUBLIC PROCEDURE [z: MDSZone] RETURNS [
heapPages, largeNodePages, increment, swapUnit: Environment.PageCount,
threshold, largeNodeThreshold: NWords, ownerChecking, checking: BOOLEAN] =
BEGIN OPEN data: LOOPHOLE[z, MDSZoneRep].data;
[
parent: , -- = Space.mds
heapPages: heapPages,
largeNodePages: largeNodePages,
increment: increment,
swapUnit: swapUnit,
threshold: threshold,
largeNodeThreshold: largeNodeThreshold,
ownerChecking: ownerChecking] ← GetAttributesHeap[@data];
END;

GetAttributesHeap: PROCEDURE [h: Handle] RETURNS [
parent: Space.Handle,
heapPages, largeNodePages, increment, swapUnit: Environment.PageCount,
threshold, largeNodeThreshold: NWords, ownerChecking, checking: BOOLEAN] =
BEGIN
CountSpace: PROCEDURE [sH: Space.Handle, isLargeNode: BOOLEAN, segH: Zone.SegmentHandle] =
BEGIN
pages: Environment.PageCount = Space.GetAttributes[sH].size;
IF isLargeNode THEN largeNodePages ← largeNodePages+pages
ELSE -- main or extension segment -- heapPages ← heapPages+pages;
END;
heapPages ← largeNodePages ← 0;
-- Be careful since Space.GetAttributes not implemented in UtilityPilot:
IF PilotSwitches.switches.u~=down THEN
EnumerateSpaces[h, CountSpace]
ELSE {heapPages ← 0; largeNodePages ← 0}; -- who cares?
RETURN[
parent: IF h.parent=hyperSpace THEN Space.nullHandle ELSE h.parent,
heapPages: heapPages,
largeNodePages: largeNodePages,
increment: h.increment,
swapUnit: h.swapUnit,
threshold: h.threshold,
largeNodeThreshold: h.largeNodeThreshold,
ownerChecking: h.ownerChecking,
checking: h.checking]
END;

Expand: PUBLIC PROCEDURE [z: UNCOUNTED ZONE, pages: Space.PageCount] =
BEGIN
ExpandHeap[LOOPHOLE[z, UncountedZoneRep].data, pages];
END;

ExpandMDS: PUBLIC PROCEDURE [z: MDSZone, pages: Space.PageCount] =
BEGIN
ExpandHeap[LOOPHOLE[z, MDSZoneRep].data, pages];
END;

ExpandHeap: PROCEDURE [h: Handle, pages: Space.PageCount] =
-- Add a segment of the specified size (or h.increment if that is larger) to h.
BEGIN
status: Zone.Status;
pages ← MAX[pages, h.increment];
[sH: , s: status] ← Zone.AddSegment[
zH: h.zH,
storage: MakeSpace[h.parent, pages, h.swapUnit, h.resident
! Space.InsufficientSpace => GOTO InsufficientSpace;
Space.Error => GOTO UnsuitableParent],
length: pages*Environment.wordsPerPage];
IF status~=okay THEN ERROR Error[otherError];
EXITS
InsufficientSpace => RETURN WITH ERROR Error[insufficientSpace];
UnsuitableParent => RETURN WITH ERROR Error[unsuitableParent];
END;

Prune: PUBLIC PROCEDURE [z: UNCOUNTED ZONE] =
BEGIN
PruneHeap[LOOPHOLE[z, UncountedZoneRep].data];
END;

PruneMDS: PUBLIC PROCEDURE [z: MDSZone] =
BEGIN
PruneHeap[LOOPHOLE[z, MDSZoneRep].data];
END;

PruneHeap: PROCEDURE [h: Handle] =
BEGIN
PruneSegment: PROCEDURE [sH: Space.Handle, isLargeNode: BOOLEAN, segH: Zone.SegmentHandle] =
BEGIN
IF ~isLargeNode AND segH~=Zone.nullSegment THEN -- only interested in extension segments
SELECT Zone.RemoveSegment[h.zH, segH].s FROM
okay => Space.Delete[sH];
nonEmptySegment => NULL;
ENDCASE => ERROR Error[invalidHeap];
END;
EnumerateSpaces[h, PruneSegment];
END;

CheckOwner: PUBLIC PROCEDURE [p: LONG POINTER] =
BEGIN
IF (p-1)↑~=Runtime.GetCaller[] THEN ERROR Error[invalidOwner];
END;

SetChecking: PUBLIC PROCEDURE [z: UNCOUNTED ZONE, checking: BOOLEAN] =
BEGIN
SetCheckingHeap[LOOPHOLE[z, UncountedZoneRep].data, checking];
END;

SetCheckingMDS: PUBLIC PROCEDURE [z: MDSZone, checking: BOOLEAN] =
BEGIN
SetCheckingHeap[LOOPHOLE[z, MDSZoneRep].data, checking];
END;

SetCheckingHeap: PROCEDURE [h: Handle, checking: BOOLEAN] =
BEGIN
IF Zone.SetChecking[zH: h.zH, checking: h.checking ← checking].s~=okay THEN
ERROR Error[invalidHeap];
END;

Error: PUBLIC ERROR [type: Heap.ErrorType] = CODE;

--
-- SpecialHeap procedures

MakeResident: PUBLIC PROCEDURE [z: UNCOUNTED ZONE] =
BEGIN
MakeResidentHeap[LOOPHOLE[z, UncountedZoneRep].data];
END;

MakeResidentMDS: PUBLIC PROCEDURE [z: MDSZone] =
BEGIN
MakeResidentHeap[LOOPHOLE[z, MDSZoneRep].data];
END;

MakeResidentHeap: PROCEDURE [h: Handle] =
BEGIN
MakeResident1: PROCEDURE [sH: Space.Handle, isLargeNode: BOOLEAN, segH: Zone.SegmentHandle] =
BEGIN SpecialSpace.MakeResident[sH] END;
EnumerateSpaces[h, MakeResident1];
h.resident ← TRUE;
END;

MakeSwappable: PUBLIC PROCEDURE [z: UNCOUNTED ZONE] =
BEGIN
MakeSwappableHeap[LOOPHOLE[z, UncountedZoneRep].data];
END;

MakeSwappableMDS: PUBLIC PROCEDURE [z: MDSZone] =
BEGIN
MakeSwappableHeap[LOOPHOLE[z, MDSZoneRep].data];
END;

MakeSwappableHeap: PROCEDURE [h: Handle] =
BEGIN
MakeSwappable1: PROCEDURE [sH: Space.Handle, isLargeNode: BOOLEAN, segH: Zone.SegmentHandle] =
BEGIN SpecialSpace.MakeSwappable[sH] END;
EnumerateSpaces[h, MakeSwappable1];
h.resident ← FALSE;
END;

--
-- Miscellaneous

EnumerateSpaces: PROCEDURE [
h: Handle, P: PROCEDURE [sH: Space.Handle, isLargeNode: BOOLEAN, segH: Zone.SegmentHandle]] =
-- Call P once for each space of heap h. There is one space for:
-- each large node (isLargeNode=TRUE, segH=nullHandle),
-- one space for the original heap (isLargeNode=FALSE, segH=nullHandle), and
-- one space for each extension segment (isLargeNode=FALSE, segH~=nullHandle).
-- Things are arranged so P may delete the space:
-- next of a large node is saved, and
-- the space for the original heap is saved for the last call on P.
BEGIN
hasSwapUnits: BOOLEAN = h.swapUnit~=defaultSwapUnit;
largeNode, nextLargeNode: LONG POINTER TO LargeNodeHeader;
firstSeg, seg: LONG POINTER;
segH, nextSegH: Zone.SegmentHandle;
-- First enumerate the large node spaces.
FOR largeNode ← h.largeNodes, nextLargeNode WHILE largeNode~=NIL DO
nextLargeNode ← largeNode.next;
P[SpaceFromLongPointer[largeNode, hasSwapUnits], TRUE, Zone.nullSegment];
ENDLOOP;
-- Now enumerate the segments, visiting the original segment last.
[storage: firstSeg, next: nextSegH] ← Zone.GetAttributes[h.zH];
-- Visit all those segments added to the zone.
WHILE nextSegH~=Zone.nullSegment DO
segH ← nextSegH;
[storage: seg, next: nextSegH] ← Zone.GetSegmentAttributes[h.zH, segH];
P[SpaceFromLongPointer[seg, hasSwapUnits], FALSE, segH];
ENDLOOP;
-- Finally visit the segment from the main zone.
P[SpaceFromLongPointer[firstSeg, hasSwapUnits], FALSE, Zone.nullSegment];
END;

FreeLargeOrRegularNode: ENTRY PROCEDURE [h: Handle, p: LONG POINTER] =
-- Free the space created by MakeLargeNode,
-- or if this is a false alarm just free a (large) normal node.
BEGIN
prev: LONG POINTER TO LONG POINTER TO LargeNodeHeader ← @h.largeNodes;
largeNode: LONG POINTER TO LargeNodeHeader;
DO
largeNode ← prev↑;
IF largeNode=NIL THEN GOTO Regular;
IF @largeNode.node=p THEN GOTO Large;
prev ← @largeNode.next;
REPEAT
Large =>
BEGIN
prev↑ ← largeNode.next;
FreeSpace[p, h.swapUnit~=defaultSwapUnit];
END;
Regular => SELECT Zone.FreeNode[zH: h.zH, p: p] FROM
okay => NULL;
invalidNode => RETURN WITH ERROR Error[invalidNode];
ENDCASE => RETURN WITH ERROR Error[invalidHeap];
ENDLOOP;
END;

FreeSpace: PROCEDURE [p: LONG POINTER, hasSwapUnits: BOOLEAN] =
-- Delete the mapped space containing the address p.
BEGIN
Space.Delete[SpaceFromLongPointer[p, hasSwapUnits]];
END;

MakeLargeNode: ENTRY PROCEDURE [h: Handle, n: NWords] RETURNS [LONG POINTER] =
-- Make a space to hold a large node, and add it to h’s largeNodes list.
BEGIN
largeNode: LONG POINTER TO LargeNodeHeader = MakeSpace[
h.parent, PagesForWords[n+SIZE[LargeNodeHeader]], h.swapUnit, h.resident
! Space.InsufficientSpace => GOTO InsufficientSpace;
Space.Error => GOTO UnsuitableParent];
largeNode↑ ← [
next: h.largeNodes,
nodeHeader: [length: n+nodeOverhead, extension: inuse[]],
node: NULL];
h.largeNodes ← largeNode;
RETURN[@largeNode.node]
EXITS
InsufficientSpace => RETURN WITH ERROR Error[insufficientSpace];
UnsuitableParent => RETURN WITH ERROR Error[unsuitableParent];
END;

MakeSpace: PROCEDURE [
parent: Space.Handle, pages, swapUnit: Environment.PageCount,
resident: BOOLEAN] RETURNS [LONG POINTER] =
-- Make a data space, and perhaps (one level of) swap units.
BEGIN
sH: Space.Handle = Space.Create[size: pages, parent: parent];
IF swapUnit~=defaultSwapUnit THEN
Space.CreateUniformSwapUnits[size: swapUnit, parent: sH];
Space.Map[sH];
IF resident THEN SpecialSpace.MakeResident[sH];
RETURN[Space.LongPointer[sH]]
END;

Narrow: PROCEDURE [p: LONG POINTER] RETURNS [POINTER] =
-- Convert a long pointer assumed to point into this mds into a short pointer.
BEGIN
RETURN[LOOPHOLE[Inline.LowHalf[p-pMDS], POINTER]]
END;

PagesForNewSegment: PROCEDURE [n: NWords] RETURNS [Space.PageCount] =
BEGIN
RETURN[PagesForWords[zoneOverhead+nodeOverhead+n]]
END;

PagesForWords: PROCEDURE [n: NWords] RETURNS [Environment.PageCount] =
-- Calculate the number of whole pages to hold n words.
INLINE BEGIN
RETURN[(n+Environment.wordsPerPage-1)/Environment.wordsPerPage]
END;

SpaceFromLongPointer: PROCEDURE [p: LONG POINTER, hasSwapUnits: BOOLEAN]
RETURNS [sh: Space.Handle] =
-- Find the mapped space containing the address p.
BEGIN
space: Space.Handle = Space.GetHandle[Space.PageFromLongPointer[p]];
IF ~hasSwapUnits THEN RETURN[space]
ELSE RETURN[Space.GetAttributes[space].parent];
END;

END.

May 23, 1980 1:45 PM
McJones
Create file

June 10, 1980 4:32 PM
McJones
Convert back to Amargosa

June 11, 1980 2:54 PM
McJones
Reconvert to Mesa 6

June 30, 1980 4:38 PM
McJones
Identify systemHeap with SystemMDSHeap if UtilityPilot

July 8, 1980 5:06 PM
McJones
Don’t catch Space.InsufficientSpace in MakeSpace

July 19, 1980 3:40 PM
McJones
New ZONE representation; fix bugs in large node detection and
shortening large node pointer in MakeMDSNode; store large node header
right in large node

July 24, 1980 10:07 AM
McJones
Identify systemHeap with SystemMDSHeap if UtilityPilot (again!)

July 26, 1980 3:24 PM
Forrest
If UtilityPilot treat large MDS nodes same as if small

August 4, 1980 6:17 PM
McJones
Large nodes and expansions of resident heap must be made resident;
EnumerateSpaces must be prepared for deletion of large node space;
repackage Expand for export

August 5, 1980 1:54 PM
McJones
systemZONE=>SystemZone; delete {Make/Free}[MDS]String procedures made
inline in Heap

December 22, 1980 1:20 PM
Gobbel
Add RETURN WITH to ERRORs; remove default for Data.seal; make
systemMDSZone initialization use ← instead of =

February 11, 1981 10:37 AM
Gobbel
Avoid using Space.GetAttributes when not necessary in
SpaceFromLongPointer.

February 12, 1981 6:48 PM
McJones
Avoid using Space.GetAttributes in GetAttributes in UtilityPilot