ack/lang/m2/libm2/Storage.mod

344 lines
7.1 KiB
Modula-2
Raw Normal View History

1988-02-19 15:54:01 +00:00
(*
(c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands.
See the copyright notice in the ACK home directory, in the file "Copyright".
*)
1987-08-19 18:07:01 +00:00
(*$R-*)
1987-05-13 14:36:45 +00:00
IMPLEMENTATION MODULE Storage;
1988-02-19 15:54:01 +00:00
(*
Module: Dynamic Storage Allocation
Author: Ceriel J.H. Jacobs
Adapted from a version in C by Hans Tebra
Version: $Header$
*)
1987-05-13 14:36:45 +00:00
(* 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, ILLBREAK;
1987-05-13 14:36:45 +00:00
FROM SYSTEM IMPORT ADDRESS, ADR;
1987-06-26 15:59:52 +00:00
FROM Traps IMPORT Message;
1987-05-13 14:36:45 +00:00
CONST
NLISTS = 20;
MAGICW = 0A5A5H;
MAGICC = 175C;
1987-05-13 14:36:45 +00:00
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:
BNEXT: BucketPtr; (* next free Bucket *)
BSIZE: CARDINAL; | (* size of user part in UNITs *)
1987-05-13 14:36:45 +00:00
TRUE: BXX: ALIGNTYPE
END;
BSTORE: ALIGNTYPE;
END;
CONST
UNIT = SIZE(ALIGNTYPE);
VAR
FreeLists: ARRAY[0..NLISTS] OF BucketPtr; (* small blocks *)
Llist: BucketPtr; (* others *)
Compacted: BOOLEAN; (* avoid recursive reorganization *)
FirstBlock: BucketPtr;
USED: ADDRESS;
1987-05-13 14:36:45 +00:00
1988-03-16 09:20:36 +00:00
PROCEDURE MyAllocate(size: CARDINAL) : ADDRESS;
VAR nu : CARDINAL;
b : CARDINAL;
1987-05-13 14:36:45 +00:00
p, q: BucketPtr;
pc: POINTER TO CHAR;
1987-05-13 14:36:45 +00:00
brk : ADDRESS;
BEGIN
IF size > CARDINAL(MAX(INTEGER)) THEN
RETURN NIL;
END;
1987-05-13 14:36:45 +00:00
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;
IF p^.BSIZE * UNIT # size THEN
pc := ADR(p^.BSTORE) + size;
pc^ := MAGICC;
END;
1987-05-13 14:36:45 +00:00
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) + (nu+1)*UNIT;
1987-05-13 14:36:45 +00:00
(* 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;
IF q^.BSIZE * UNIT # size THEN
pc := ADR(q^.BSTORE) + size;
pc^ := MAGICC;
END;
1987-05-13 14:36:45 +00:00
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) + (nu+1)*UNIT;
1987-05-13 14:36:45 +00:00
q^.BSIZE := p^.BSIZE -nu -1;
q^.BNEXT := FreeLists[q^.BSIZE];
FreeLists[q^.BSIZE] := q;
p^.BSIZE := nu;
END;
IF p^.BSIZE * UNIT # size THEN
pc := ADR(p^.BSTORE) + size;
pc^ := MAGICC;
END;
1987-05-13 14:36:45 +00:00
RETURN ADR(p^.BSTORE);
END;
(* Give part of tail of original block.
Block stays in this list.
*)
q := ADDRESS(p) + (p^.BSIZE-nu)*UNIT;
1987-05-13 14:36:45 +00:00
q^.BSIZE := nu;
p^.BSIZE := p^.BSIZE - nu - 1;
q^.BNEXT := USED;
IF q^.BSIZE * UNIT # size THEN
pc := ADR(q^.BSTORE) + size;
pc^ := MAGICC;
END;
1987-05-13 14:36:45 +00:00
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;
1988-03-16 09:20:36 +00:00
brk := MyAllocate(size);
1987-05-13 14:36:45 +00:00
Compacted := FALSE;
RETURN brk;
END;
p := brk;
p^.BSIZE := nu;
p^.BNEXT := USED;
IF p^.BSIZE * UNIT # size THEN
pc := ADR(p^.BSTORE) + size;
pc^ := MAGICC;
END;
1987-05-13 14:36:45 +00:00
RETURN ADR(p^.BSTORE);
1988-03-16 09:20:36 +00:00
END MyAllocate;
PROCEDURE Allocate(VAR a: ADDRESS; size: CARDINAL);
BEGIN
ALLOCATE(a, size);
1987-05-13 14:36:45 +00:00
END Allocate;
PROCEDURE ALLOCATE(VAR a: ADDRESS; size: CARDINAL);
BEGIN
1988-03-16 09:20:36 +00:00
a := MyAllocate(size);
1987-05-13 14:36:45 +00:00
IF a = NIL THEN
1987-06-26 15:59:52 +00:00
Message("out of core");
HALT;
1987-05-13 14:36:45 +00:00
END;
END ALLOCATE;
PROCEDURE Available(size: CARDINAL): BOOLEAN;
VAR a: ADDRESS;
BEGIN
1988-03-16 09:20:36 +00:00
a:= MyAllocate(size);
1987-05-13 14:36:45 +00:00
IF a # NIL THEN
DEALLOCATE(a, size);
RETURN TRUE;
END;
RETURN FALSE;
END Available;
1988-03-16 09:20:36 +00:00
PROCEDURE Deallocate(VAR a: ADDRESS; size: CARDINAL);
BEGIN
DEALLOCATE(a, size);
END Deallocate;
1987-05-13 14:36:45 +00:00
PROCEDURE DEALLOCATE(VAR a: ADDRESS; size: CARDINAL);
VAR p: BucketPtr;
pc: POINTER TO CHAR;
1987-05-13 14:36:45 +00:00
BEGIN
IF (a = NIL) THEN
Message("(Warning) NIL pointer deallocated");
RETURN;
END;
1987-05-13 14:36:45 +00:00
p := a - UNIT;
IF (p^.BNEXT # BucketPtr(USED)) THEN
Message("(Warning) area already deallocated or heap corrupted");
a := NIL;
RETURN;
END;
1987-05-13 14:36:45 +00:00
WITH p^ DO
IF BSIZE # ((size + (UNIT - 1)) DIV UNIT) THEN
Message("(Warning) wrong size in deallocate");
ELSIF (BSIZE*UNIT # size) THEN
pc := a + size;
IF pc^ # MAGICC THEN
Message("(Warning) heap corrupted or wrong size in deallocate");
END;
END;
1987-05-13 14:36:45 +00:00
IF BSIZE <= NLISTS THEN
BNEXT := FreeLists[BSIZE];
FreeLists[BSIZE] := p;
ELSE
BNEXT := Llist;
Llist := p;
END;
END;
a := NIL
1987-05-13 14:36:45 +00:00
END DEALLOCATE;
PROCEDURE ReOrganize();
VAR lastblock: BucketPtr;
b, be: BucketPtr;
i: CARDINAL;
1987-05-13 14:36:45 +00:00
BEGIN
lastblock := NIL;
1987-05-13 14:36:45 +00:00
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)+(b^.BSIZE+1)*UNIT;
1987-05-13 14:36:45 +00:00
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, but only if it is not too big
1987-05-13 14:36:45 +00:00
*)
IF MAX(CARDINAL) - b^.BSIZE > be^.BSIZE THEN
b^.BSIZE := b^.BSIZE + be^.BSIZE + 1;
ELSE
EXIT;
END;
1987-05-13 14:36:45 +00:00
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) + (b^.BSIZE+1) * UNIT;
1987-05-13 14:36:45 +00:00
END;
END ReOrganize;
PROCEDURE InitStorage();
VAR i: CARDINAL;
1987-05-13 14:36:45 +00:00
brk: ADDRESS;
BEGIN
FOR i := 1 TO NLISTS DO
FreeLists[i] := NIL;
END;
Llist := NIL;
brk := sbrk(0);
brk := sbrk(UNIT - brk MOD UNIT);
1987-05-13 14:36:45 +00:00
FirstBlock := sbrk(0);
Compacted := FALSE;
USED := MAGICW;
1987-05-13 14:36:45 +00:00
END InitStorage;
BEGIN
InitStorage();
END Storage.