276 lines
5.6 KiB
Modula-2
276 lines
5.6 KiB
Modula-2
|
IMPLEMENTATION MODULE Storage;
|
||
|
(* This storage manager maintains an array of lists of objects with the
|
||
|
same size. Commonly used sizes have their own bucket. The larger ones
|
||
|
are put in a single list.
|
||
|
*)
|
||
|
FROM Unix IMPORT sbrk, write, exit, ILLBREAK;
|
||
|
FROM SYSTEM IMPORT ADDRESS, ADR;
|
||
|
|
||
|
CONST
|
||
|
NLISTS = 20;
|
||
|
|
||
|
TYPE
|
||
|
ALIGNTYPE =
|
||
|
RECORD
|
||
|
CASE : INTEGER OF
|
||
|
1: l: LONGINT |
|
||
|
2: p: ADDRESS |
|
||
|
3: d: LONGREAL
|
||
|
END
|
||
|
END; (* A type with high alignment requirements *)
|
||
|
BucketPtr = POINTER TO Bucket;
|
||
|
Bucket =
|
||
|
RECORD
|
||
|
CASE : BOOLEAN OF
|
||
|
FALSE: BSIZE: INTEGER; (* size of user part in UNITs *)
|
||
|
BNEXT: BucketPtr; | (* next free Bucket *)
|
||
|
TRUE: BXX: ALIGNTYPE
|
||
|
END;
|
||
|
BSTORE: ALIGNTYPE;
|
||
|
END;
|
||
|
|
||
|
CONST
|
||
|
UNIT = SIZE(ALIGNTYPE);
|
||
|
USED = BucketPtr(1);
|
||
|
|
||
|
VAR
|
||
|
FreeLists: ARRAY[0..NLISTS] OF BucketPtr; (* small blocks *)
|
||
|
Llist: BucketPtr; (* others *)
|
||
|
Compacted: BOOLEAN; (* avoid recursive reorganization *)
|
||
|
FirstBlock: BucketPtr;
|
||
|
|
||
|
PROCEDURE Allocate(size: CARDINAL) : ADDRESS;
|
||
|
VAR nu : INTEGER;
|
||
|
b : INTEGER;
|
||
|
p, q: BucketPtr;
|
||
|
brk : ADDRESS;
|
||
|
BEGIN
|
||
|
nu := (size + (UNIT-1)) DIV UNIT;
|
||
|
IF nu = 0 THEN
|
||
|
RETURN NIL;
|
||
|
END;
|
||
|
IF nu <= NLISTS THEN
|
||
|
b := nu;
|
||
|
IF FreeLists[b] # NIL THEN
|
||
|
(* Exact fit *)
|
||
|
p := FreeLists[b];
|
||
|
FreeLists[b] := p^.BNEXT;
|
||
|
p^.BNEXT := USED;
|
||
|
RETURN ADR(p^.BSTORE);
|
||
|
END;
|
||
|
|
||
|
(* Search for a block with >= 2 units more than requested.
|
||
|
We pay for an additional header when the block is split.
|
||
|
*)
|
||
|
FOR b := b+2 TO NLISTS DO
|
||
|
IF FreeLists[b] # NIL THEN
|
||
|
q := FreeLists[b];
|
||
|
FreeLists[b] := q^.BNEXT;
|
||
|
p := ADDRESS(q) + CARDINAL((nu+1)*UNIT);
|
||
|
(* p indicates the block that must be given
|
||
|
back
|
||
|
*)
|
||
|
p^.BSIZE := q^.BSIZE - nu - 1;
|
||
|
p^.BNEXT := FreeLists[p^.BSIZE];
|
||
|
FreeLists[p^.BSIZE] := p;
|
||
|
q^.BSIZE := nu;
|
||
|
q^.BNEXT := USED;
|
||
|
RETURN ADR(q^.BSTORE);
|
||
|
END;
|
||
|
END;
|
||
|
END;
|
||
|
|
||
|
p := Llist;
|
||
|
IF p # NIL THEN
|
||
|
q := NIL;
|
||
|
WHILE (p # NIL) AND (p^.BSIZE < nu) DO
|
||
|
q := p;
|
||
|
p := p^.BNEXT;
|
||
|
END;
|
||
|
|
||
|
IF p # NIL THEN
|
||
|
(* p^.BSIZE >= nu *)
|
||
|
IF p^.BSIZE <= nu + NLISTS + 1 THEN
|
||
|
(* Remove p from this list *)
|
||
|
IF q # NIL THEN q^.BNEXT := p^.BNEXT
|
||
|
ELSE Llist := p^.BNEXT;
|
||
|
END;
|
||
|
p^.BNEXT := USED;
|
||
|
IF p^.BSIZE > nu + 1 THEN
|
||
|
(* split block,
|
||
|
tail goes to FreeLists area
|
||
|
*)
|
||
|
q := ADDRESS(p) + CARDINAL((nu+1)*UNIT);
|
||
|
q^.BSIZE := p^.BSIZE -nu -1;
|
||
|
q^.BNEXT := FreeLists[q^.BSIZE];
|
||
|
FreeLists[q^.BSIZE] := q;
|
||
|
p^.BSIZE := nu;
|
||
|
END;
|
||
|
RETURN ADR(p^.BSTORE);
|
||
|
END;
|
||
|
(* Give part of tail of original block.
|
||
|
Block stays in this list.
|
||
|
*)
|
||
|
q := ADDRESS(p) + CARDINAL((p^.BSIZE-nu)*UNIT);
|
||
|
q^.BSIZE := nu;
|
||
|
p^.BSIZE := p^.BSIZE - nu - 1;
|
||
|
q^.BNEXT := USED;
|
||
|
RETURN ADR(q^.BSTORE);
|
||
|
END;
|
||
|
END;
|
||
|
|
||
|
IF Compacted THEN
|
||
|
(* reorganization did not yield sufficient memory *)
|
||
|
RETURN NIL;
|
||
|
END;
|
||
|
|
||
|
brk := sbrk(UNIT * (nu + 1));
|
||
|
IF brk = ILLBREAK THEN
|
||
|
ReOrganize();
|
||
|
Compacted := TRUE;
|
||
|
brk := Allocate(size);
|
||
|
Compacted := FALSE;
|
||
|
RETURN brk;
|
||
|
END;
|
||
|
|
||
|
p := brk;
|
||
|
p^.BSIZE := nu;
|
||
|
p^.BNEXT := USED;
|
||
|
RETURN ADR(p^.BSTORE);
|
||
|
END Allocate;
|
||
|
|
||
|
PROCEDURE ALLOCATE(VAR a: ADDRESS; size: CARDINAL);
|
||
|
VAR err: ARRAY[0..20] OF CHAR;
|
||
|
BEGIN
|
||
|
a := Allocate(size);
|
||
|
IF a = NIL THEN
|
||
|
err:= "Out of core";
|
||
|
err[11] := 12C;
|
||
|
IF write(2, ADR(err), 12) < 0 THEN
|
||
|
;
|
||
|
END;
|
||
|
exit(1);
|
||
|
END;
|
||
|
END ALLOCATE;
|
||
|
|
||
|
PROCEDURE Available(size: CARDINAL): BOOLEAN;
|
||
|
VAR a: ADDRESS;
|
||
|
BEGIN
|
||
|
a:= Allocate(size);
|
||
|
IF a # NIL THEN
|
||
|
DEALLOCATE(a, size);
|
||
|
RETURN TRUE;
|
||
|
END;
|
||
|
RETURN FALSE;
|
||
|
END Available;
|
||
|
|
||
|
PROCEDURE DEALLOCATE(VAR a: ADDRESS; size: CARDINAL);
|
||
|
VAR p: BucketPtr;
|
||
|
BEGIN
|
||
|
IF (a = NIL) THEN RETURN; END;
|
||
|
p := a - UNIT;
|
||
|
IF (p^.BNEXT # USED) THEN RETURN; END;
|
||
|
WITH p^ DO
|
||
|
IF BSIZE <= NLISTS THEN
|
||
|
BNEXT := FreeLists[BSIZE];
|
||
|
FreeLists[BSIZE] := p;
|
||
|
ELSE
|
||
|
BNEXT := Llist;
|
||
|
Llist := p;
|
||
|
END;
|
||
|
END;
|
||
|
END DEALLOCATE;
|
||
|
|
||
|
PROCEDURE ReOrganize();
|
||
|
VAR lastblock: BucketPtr;
|
||
|
b, be: BucketPtr;
|
||
|
i: INTEGER;
|
||
|
BEGIN
|
||
|
FOR i := 1 TO NLISTS DO
|
||
|
b := FreeLists[i];
|
||
|
WHILE b # NIL DO
|
||
|
IF ADDRESS(b) > ADDRESS(lastblock) THEN
|
||
|
lastblock := b;
|
||
|
END;
|
||
|
be := b^.BNEXT;
|
||
|
b^.BNEXT := NIL; (* temporary free mark *)
|
||
|
b := be;
|
||
|
END;
|
||
|
END;
|
||
|
|
||
|
b := Llist;
|
||
|
WHILE b # NIL DO
|
||
|
IF ADDRESS(b) > ADDRESS(lastblock) THEN
|
||
|
lastblock := b;
|
||
|
END;
|
||
|
be := b^.BNEXT;
|
||
|
b^.BNEXT := NIL;
|
||
|
b := be;
|
||
|
END;
|
||
|
|
||
|
(* Now, all free blocks have b^.BNEXT = NIL *)
|
||
|
|
||
|
b := FirstBlock;
|
||
|
WHILE ADDRESS(b) < ADDRESS(lastblock) DO
|
||
|
LOOP
|
||
|
be := ADDRESS(b)+CARDINAL((b^.BSIZE+1)*UNIT);
|
||
|
IF b^.BNEXT # NIL THEN
|
||
|
(* this block is not free *)
|
||
|
EXIT;
|
||
|
END;
|
||
|
IF ADDRESS(be) > ADDRESS(lastblock) THEN
|
||
|
(* no next block *)
|
||
|
EXIT;
|
||
|
END;
|
||
|
IF be^.BNEXT # NIL THEN
|
||
|
(* next block is not free *)
|
||
|
EXIT;
|
||
|
END;
|
||
|
(* this block and the next one are free,
|
||
|
so merge them
|
||
|
*)
|
||
|
b^.BSIZE := b^.BSIZE + be^.BSIZE + 1;
|
||
|
END;
|
||
|
b := be;
|
||
|
END;
|
||
|
|
||
|
(* clear all free lists *)
|
||
|
FOR i := 1 TO NLISTS DO FreeLists[i] := NIL; END;
|
||
|
Llist := NIL;
|
||
|
|
||
|
(* collect free blocks in them again *)
|
||
|
b := FirstBlock;
|
||
|
WHILE ADDRESS(b) <= ADDRESS(lastblock) DO
|
||
|
WITH b^ DO
|
||
|
IF BNEXT = NIL THEN
|
||
|
IF BSIZE <= NLISTS THEN
|
||
|
BNEXT := FreeLists[BSIZE];
|
||
|
FreeLists[BSIZE] := b;
|
||
|
ELSE
|
||
|
BNEXT := Llist;
|
||
|
Llist := b;
|
||
|
END;
|
||
|
END;
|
||
|
END;
|
||
|
b := ADDRESS(b) + CARDINAL((b^.BSIZE+1) * UNIT);
|
||
|
END;
|
||
|
END ReOrganize;
|
||
|
|
||
|
PROCEDURE InitStorage();
|
||
|
VAR i: INTEGER;
|
||
|
brk: ADDRESS;
|
||
|
BEGIN
|
||
|
FOR i := 1 TO NLISTS DO
|
||
|
FreeLists[i] := NIL;
|
||
|
END;
|
||
|
Llist := NIL;
|
||
|
brk := sbrk(0);
|
||
|
brk := sbrk(UNIT - INTEGER(brk MOD UNIT));
|
||
|
FirstBlock := sbrk(0);
|
||
|
Compacted := FALSE;
|
||
|
END InitStorage;
|
||
|
|
||
|
BEGIN
|
||
|
InitStorage();
|
||
|
END Storage.
|