353 lines
		
	
	
	
		
			7.3 KiB
		
	
	
	
		
			Modula-2
		
	
	
	
	
	
			
		
		
	
	
			353 lines
		
	
	
	
		
			7.3 KiB
		
	
	
	
		
			Modula-2
		
	
	
	
	
	
| (*
 | |
|   (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands.
 | |
|   See the copyright notice in the ACK home directory, in the file "Copyright".
 | |
| *)
 | |
| 
 | |
| (*$R-*)
 | |
| IMPLEMENTATION MODULE Storage;
 | |
| (*
 | |
|   Module:	Dynamic Storage Allocation
 | |
|   Author:	Ceriel J.H. Jacobs
 | |
| 		Adapted from a version in C by Hans Tebra
 | |
|   Version:	$Header$
 | |
| *)
 | |
| (* 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;
 | |
|   FROM SYSTEM IMPORT ADDRESS, ADR;
 | |
|   FROM Traps IMPORT Message;
 | |
| 
 | |
|   CONST
 | |
| 	NLISTS = 20;
 | |
| 	MAGICW = 0A5A5H;
 | |
| 	MAGICC = 175C;
 | |
| 
 | |
|   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 *)
 | |
| 		   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;
 | |
| 
 | |
|   PROCEDURE MyAllocate(size: CARDINAL) : ADDRESS;
 | |
|     VAR	nu : CARDINAL;
 | |
| 	b : CARDINAL;
 | |
| 	p, q: BucketPtr;
 | |
| 	pc: POINTER TO CHAR;
 | |
| 	brk : ADDRESS;
 | |
|   BEGIN
 | |
| 	IF size > CARDINAL(MAX(INTEGER)-2*UNIT + 1) THEN
 | |
| 		RETURN NIL;
 | |
| 	END;
 | |
| 	nu := (size + (UNIT-1)) DIV UNIT;
 | |
| 	IF nu = 0 THEN
 | |
| 		nu := 1;
 | |
| 	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;
 | |
| 			p^.BSIZE := size;
 | |
| 			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;
 | |
| 				(* 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;
 | |
| 				q^.BSIZE := size;
 | |
| 				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;
 | |
| 					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;
 | |
| 				p^.BSIZE := size;
 | |
| 				RETURN ADR(p^.BSTORE);
 | |
| 			END;
 | |
| 			(* Give part of tail of original block.
 | |
| 			   Block stays in this list.
 | |
| 			*)
 | |
| 			q := ADDRESS(p) + (p^.BSIZE-nu)*UNIT;
 | |
| 			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;
 | |
| 			q^.BSIZE := size;
 | |
| 			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 := MyAllocate(size);
 | |
| 		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;
 | |
| 	p^.BSIZE := size;
 | |
| 	RETURN ADR(p^.BSTORE);
 | |
|   END MyAllocate;
 | |
| 
 | |
|   PROCEDURE ALLOCATE(VAR a: ADDRESS; size: CARDINAL);
 | |
|   BEGIN
 | |
| 	Allocate(a, size);
 | |
|   END ALLOCATE;
 | |
| 
 | |
|   PROCEDURE Allocate(VAR a: ADDRESS; size: CARDINAL);
 | |
|   BEGIN
 | |
| 	a := MyAllocate(size);
 | |
| 	IF a = NIL THEN
 | |
| 		Message("out of core");
 | |
| 		HALT;
 | |
| 	END;
 | |
|   END Allocate;
 | |
| 
 | |
|   PROCEDURE Available(size: CARDINAL): BOOLEAN;
 | |
|     VAR	a: ADDRESS;
 | |
|   BEGIN
 | |
| 	a:= MyAllocate(size);
 | |
| 	IF a # NIL THEN
 | |
| 		Deallocate(a, size);
 | |
| 		RETURN TRUE;
 | |
| 	END;
 | |
| 	RETURN FALSE;
 | |
|   END Available;
 | |
| 
 | |
|   PROCEDURE DEALLOCATE(VAR a: ADDRESS; size: CARDINAL);
 | |
|   BEGIN
 | |
| 	Deallocate(a, size);
 | |
|   END DEALLOCATE;
 | |
| 
 | |
|   PROCEDURE Deallocate(VAR a: ADDRESS; size: CARDINAL);
 | |
|     VAR	p: BucketPtr;
 | |
| 	pc: POINTER TO CHAR;
 | |
|   BEGIN
 | |
| 	IF (a = NIL) THEN 
 | |
| 		Message("(Warning) Deallocate: NIL pointer deallocated");
 | |
| 		RETURN;
 | |
| 	END;
 | |
| 	p := a - UNIT;
 | |
| 	IF (p^.BNEXT # BucketPtr(USED)) THEN
 | |
| 		Message("(Warning) Deallocate: area already deallocated or heap corrupted");
 | |
| 		a := NIL;
 | |
| 		RETURN;
 | |
| 	END;
 | |
| 	WITH p^ DO
 | |
| 		IF BSIZE # size THEN
 | |
| 			Message("(Warning) Deallocate: wrong size or heap corrupted");
 | |
| 		END;
 | |
| 		BSIZE := (size + (UNIT - 1)) DIV UNIT;
 | |
| 		IF (BSIZE*UNIT # size) THEN
 | |
| 			pc := a + size;
 | |
| 			IF pc^ # MAGICC THEN
 | |
| 				Message("(Warning) Deallocate: heap corrupted");
 | |
| 			END;
 | |
| 		END;	
 | |
| 		IF BSIZE <= NLISTS THEN
 | |
| 			BNEXT := FreeLists[BSIZE];
 | |
| 			FreeLists[BSIZE] := p;
 | |
| 		ELSE
 | |
| 			BNEXT := Llist;
 | |
| 			Llist := p;
 | |
| 		END;
 | |
| 	END;
 | |
| 	a := NIL
 | |
|   END Deallocate;
 | |
| 
 | |
|   PROCEDURE ReOrganize();
 | |
|     VAR lastblock: BucketPtr;
 | |
| 	b, be: BucketPtr;
 | |
| 	i: CARDINAL;
 | |
|   BEGIN
 | |
| 	lastblock := NIL;
 | |
| 	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;
 | |
| 			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
 | |
| 			*)
 | |
| 			IF MAX(CARDINAL) - b^.BSIZE > be^.BSIZE THEN
 | |
| 				b^.BSIZE := b^.BSIZE + be^.BSIZE + 1;
 | |
| 			ELSE
 | |
| 				EXIT;
 | |
| 			END;
 | |
| 		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;
 | |
| 				b := ADDRESS(b) + (BSIZE+1) * UNIT;
 | |
| 			ELSE
 | |
| 				b := ADDRESS(b) + 
 | |
| 					((BSIZE + (UNIT - 1)) DIV UNIT + 1) * UNIT;
 | |
| 			END;
 | |
| 		END;
 | |
| 	END;
 | |
|   END ReOrganize;
 | |
| 
 | |
|   PROCEDURE InitStorage();
 | |
|     VAR	i: CARDINAL;
 | |
| 	brk: ADDRESS;
 | |
|   BEGIN
 | |
| 	FOR i := 1 TO NLISTS DO
 | |
| 		FreeLists[i] := NIL;
 | |
| 	END;
 | |
| 	Llist := NIL;
 | |
| 	brk := sbrk(0);
 | |
| 	brk := sbrk(UNIT - brk MOD UNIT);
 | |
| 	FirstBlock := sbrk(0);
 | |
| 	Compacted := FALSE;
 | |
| 	USED := MAGICW;
 | |
|   END InitStorage;
 | |
| 
 | |
| BEGIN
 | |
| 	InitStorage();
 | |
| END Storage.
 |