Ver código fonte

Initial revision

ceriel 37 anos atrás
pai
commit
12869eaa07

+ 38 - 0
modules/src/malloc/Makefile

@@ -0,0 +1,38 @@
+EMHOME = ../../..
+INSTALL = $(EMHOME)/modules/install
+COMPARE = $(EMHOME)/modules/compare
+CFLAGS = -O -I$(EMHOME)/modules/h
+
+MALLOCSRC =	READ_ME size_type.h param.h impl.h check.h log.h phys.h \
+		mal.c log.c phys.c check.c
+
+all:		malloc.o
+
+install:	all
+		$(INSTALL) lib/malloc.o
+
+cmp:		all
+		$(COMPARE) lib/malloc.o
+
+malloc1.c:	$(MALLOCSRC) Makefile add_file
+		rm -f malloc1.c
+		for i in $(MALLOCSRC) ; do add_file $$i >> malloc1.c ; done
+
+malloc.c:	malloc1.c
+		cclash -l7 -c malloc1.c > clashes
+		cid -Fclashes < malloc1.c > malloc.c
+
+pr:
+		@pr Makefile add_file $(MALLOCSRC)
+
+opr:
+		make pr | opr
+
+clean:
+		rm -f *.o clashes malloc1.c size_type.h getsize malloc.c
+
+size_type.h:	getsize
+		getsize > size_type.h
+
+getsize:	getsize.o
+		$(CC) -o getsize getsize.o

+ 27 - 0
modules/src/malloc/READ_ME

@@ -0,0 +1,27 @@
+/*
+	PROGRAM
+		malloc(), free(), realloc()
+	AUTHOR
+		Dick Grune, Free University, Amsterdam
+		Modified by Ceriel Jacobs, Free University, Amsterdam,
+		to make it faster
+	VERSION
+		$Header$
+	DESCRIPTION
+	This is an independent rewrite of the malloc/free package; it is
+	fast and efficient.  Free blocks are kept in doubly linked lists,
+	list N holding blocks with sizes between 2**N and 2**(N+1)-1.
+	Consequently neither malloc nor free have to do any searching:
+	the cost of a call of malloc() (or free()) is constant, however
+	many blocks you have got.
+	
+	If you switch on the NON_STANDARD macro (see param.h) every block
+	costs 2 pointers overhead (otherwise it's 4).
+*/
+/*
+	There is an organisational problem here: during devellopment
+	I want the package divided into modules, which implies external
+	names for the communication.  The only external names I want in
+	the finished product are malloc, realloc and free.  This requires
+	some hanky-panky.
+*/

+ 14 - 0
modules/src/malloc/add_file

@@ -0,0 +1,14 @@
+echo ''
+echo '/**********************************************************/'
+echo '/*'
+echo '/*		' This was file $1
+echo '/*'
+echo '/**********************************************************/'
+echo ''
+cat $1 |
+sed '
+	/#include[ 	].*"/d
+	s/^public/private/
+	s/^publicdata/static/
+'
+echo ''

+ 297 - 0
modules/src/malloc/check.c

@@ -0,0 +1,297 @@
+#include	<stdio.h>
+#include	"param.h"
+#include	"impl.h"
+#include	"check.h"
+#include	"phys.h"
+#include	"log.h"
+
+#ifdef	CHECK			/* otherwise this whole file is skipped */
+
+private acquire_malout(), check_ml_last();
+private dump_all_mallinks(), dump_free_list(), dump_mallink(), print_loop();
+private working_on();
+private unsigned int checksum();
+static FILE *malout;
+
+public mallink *free_list_entry();
+
+#define	for_free_list(i,p) \
+	for (p = free_list_entry(i); p; p = log_next_of(p))
+
+#define	for_all_mallinks(ml)	/* backwards! */ \
+	for (ml = ml_last; ml; \
+		ml = first_mallink(ml) ? MAL_NULL : phys_prev_of(ml))
+
+/* Maldump */
+
+static int pr_cnt = 0;
+
+maldump(n)	{
+	/*	Dump pertinent info in pseudo-readable format;
+		abort afterwards if n != 0.
+	*/
+	static int dumping = 0;
+	int i;
+	
+	if (dumping)
+		return;
+	dumping++;
+	acquire_malout();
+	fprintf(malout,
+		">>>>>>>>>>>>>>>> DUMP OF ALL MALLINKS <<<<<<<<<<<<<<<<");
+	fprintf(malout, "    ml_last = %ld\n", (long)ml_last);
+	if (++pr_cnt == 100) pr_cnt = 0;
+	dump_all_mallinks();
+	fprintf(malout,
+		">>>>>>>>>>>>>>>> DUMP OF FREE_LISTS <<<<<<<<<<<<<<<<\n");
+	if (++pr_cnt == 100) pr_cnt = 0;
+	for (i = 0; i < MAX_FLIST; i++)
+		dump_free_list(i);
+	fprintf(malout,
+		">>>>>>>>>>>>>>>> END OF DUMP <<<<<<<<<<<<<<<<\n");
+	fclose(malout);
+	dumping--;
+	if (n)
+		abort();
+}
+
+private
+acquire_malout()	{
+	static char buf[BUFSIZ];
+	
+	if (!malout)	{
+		malout = fopen("mal.out", "w");	
+		setbuf(malout, buf);
+	}
+}
+
+private
+dump_all_mallinks()	{
+	mallink *ml;
+	
+	for_all_mallinks (ml)	{
+		if (print_loop(ml))
+			return;
+		dump_mallink((char *)0, ml);
+	}
+}
+
+private
+dump_free_list(i)	{
+	mallink *ml = free_list_entry(i);
+	
+	if (!ml)
+		return;
+	fprintf(malout, "%2d: ", i);
+	for_free_list(i, ml)	{
+		if (print_loop(ml))
+			return;
+		fprintf(malout, "%ld ", ml);
+	}
+	fprintf(malout, "<\n");
+}
+
+private int
+print_loop(ml) mallink *ml;	{
+	if (print_of(ml) == pr_cnt)	{
+		fprintf(malout, "... PRINT LOOP\n");
+		return 1;
+	}
+	set_print(ml, pr_cnt);
+	return 0;
+}
+
+private
+dump_mallink(s, ml) char *s; mallink *ml;	{
+	acquire_malout();
+	if (s)
+		fprintf(malout, "%s: ", s);
+	fprintf(malout, "@: %ld;", (long)ml);
+	if (ml && checksum_of(ml) != checksum(ml))
+		fprintf(malout, ">>>> CORRUPTED <<<<");
+	if (!ml)	{
+		fprintf(malout, "\n");
+		return;
+	}	
+	if (free_of(ml))	{
+		fprintf(malout, " l_p: %ld;", (long)_log_prev_of(ml));
+		fprintf(malout, " l_n: %ld;", (long)_log_next_of(ml));
+	}
+	fprintf(malout, " p_s: %ld;", (long)prev_size_of(ml));
+	fprintf(malout, " t_s: %ld;", (long)_this_size_of(ml));
+	fprintf(malout, " sz: %ld;", (long)size_of(ml));
+	fprintf(malout, " fr: %d;", free_of(ml));
+	fprintf(malout, "\n");
+}
+
+/*	Check_mallinks() checks the total data structure as accessible
+	through free_list[] and ml_last.  All check_sums should be OK,
+	except those held in the small array off_colour.  This is a
+	trick to allow to continue checking even when a few mallinks
+	are temporarily out of order.
+	Check_mallinks() tests for a lot of internal consistency.
+*/
+
+/* Some arbitrary constants */
+#define	IN_ML_LAST	93
+#define	IN_FREE_LIST	57		/* and in ml_last */
+#define	CLEAR		21
+
+#define	VRIJ		1
+#define	BEZET		2
+
+public
+check_mallinks(s) char *s;	{
+	mallink *ml;
+	unsigned int size;
+	int i;
+	char stat;
+	
+	check_ml_last(s);
+	stat = BEZET;
+	for_all_mallinks(ml)	{
+		if (checksum_of(ml) != checksum(ml))
+			Error("mallink info at %ld corrupted", s, ml);
+		if (working_on(ml))	{
+			stat = BEZET;
+			continue;
+		}
+		if (	!last_mallink(ml) &&
+			phys_prev_of(phys_next_of(ml)) != ml
+		)
+			Error("upward chain bad at %ld", s, ml);
+		if (	!first_mallink(ml) &&
+			phys_next_of(phys_prev_of(ml)) != ml
+		)
+			Error("downward chain bad at %ld", s, ml);
+		if (free_of(ml))	{
+			if (stat == VRIJ)
+				Error("free mallink at %ld follows free mallink",
+								s, ml);
+			stat = VRIJ;
+		}
+		else
+			stat = BEZET;
+		set_mark(ml, IN_ML_LAST);
+	}
+	
+	for (i = 0, size = MIN_SIZE; i < MAX_FLIST; i++, size *= 2)	{
+		for_free_list(i, ml)	{
+			if (working_on(ml))
+				continue;
+			if (!free_of(ml))
+				Error("occupied mallink %ld occurs in free_list", s, ml);
+			switch (mark_of(ml))	{
+			case IN_ML_LAST:
+				set_mark(ml, IN_FREE_LIST);
+				break;
+			case IN_FREE_LIST:
+				Error("mallink %ld occurs in 2 free_lists",
+								s, ml);
+			default:
+				Error("unknown mallink %ld in free_list",
+								s, ml);
+			}
+			if (size_of(ml) < size)
+				Error("size of mallink %ld too small", s, ml);
+			if (size_of(ml) >= 2*size)
+				Error("size of mallink %ld too large", s, ml);
+		}
+	}
+	for_all_mallinks (ml)	{
+		if (working_on(ml))
+			continue;
+		if (free_of(ml) && mark_of(ml) != IN_FREE_LIST)
+			Error("free mallink %ld is in no free_list", s, ml);
+		set_mark(ml, CLEAR);
+	}
+}
+
+private
+check_ml_last(s) char *s;	{
+	if (ml_last && _this_size_of(ml_last) == 0)
+		Error("size of ml_last == 0, at %ld", s, ml_last);
+}
+
+private unsigned int
+checksum(ml) mallink *ml;	{
+	unsigned int sum = 0;
+	
+	if (free_of(ml))	{
+		sum += (unsigned int)_log_prev_of(ml);
+		sum += (unsigned int)_log_next_of(ml);
+	}
+	sum += (unsigned int)prev_size_of(ml);
+	sum += (unsigned int)_this_size_of(ml);
+	return sum;
+}
+
+public
+calc_checksum(ml) mallink *ml;	{
+	set_checksum(ml, checksum(ml));
+}
+
+#define	N_COLOUR	10
+static mallink *off_colour[N_COLOUR];
+
+public
+started_working_on(ml) mallink *ml;	{
+	int i;
+	
+	for (i = 0; i < N_COLOUR; i++)
+		if (off_colour[i] == MAL_NULL)	{
+			off_colour[i] = ml;
+			return;
+		}
+	Error("out of off_colour array at %ld", "started_working_on", ml);
+}
+
+public
+stopped_working_on(ml) mallink *ml;	{
+	int i;
+	
+	for (i = 0; i < N_COLOUR; i++)
+		if (off_colour[i] == ml)	{
+			off_colour[i] = MAL_NULL;
+			return;
+		}
+	Error("stopped working on mallink %ld", "stopped_working_on", ml);
+}
+
+private int
+working_on(ml) mallink *ml;	{
+	int i;
+	
+	for (i = 0; i < N_COLOUR; i++)
+		if (off_colour[i] == ml)
+			return 1;
+	return 0;
+}
+
+public
+check_work_empty(s) char *s;	{
+	int i;
+	int cnt = 0;
+	
+	for (i = 0; i < N_COLOUR; i++)
+		if (off_colour[i] != MAL_NULL)
+			cnt++;
+	if (cnt != 0)
+		Error("off_colour not empty", s, MAL_NULL);
+}
+
+public int
+Error(fmt, s, ml) char *fmt, *s; mallink *ml;	{
+	printf("%s: ", s);
+	printf(fmt, (long)ml);
+	printf("\n");
+	acquire_malout();
+	fprintf(malout, "%s: ", s);
+	fprintf(malout, fmt, (long)ml);
+	fprintf(malout, "\n");
+	fflush(stdout);
+	maldump(1);
+	return 0;			/* to satisfy lint */
+}
+
+#endif	CHECK

+ 15 - 0
modules/src/malloc/check.h

@@ -0,0 +1,15 @@
+#ifdef	CHECK
+
+public check_mallinks(), calc_checksum(), check_work_empty();
+public started_working_on(), stopped_working_on();
+
+#else	ifndef	CHECK
+
+#define	maldump(n)		abort()
+#define	check_mallinks(s)	0
+#define	calc_checksum(ml)	0
+#define	started_working_on(ml)	0
+#define	stopped_working_on(ml)	0
+#define	check_work_empty(s)	0
+
+#endif	CHECK

+ 19 - 0
modules/src/malloc/getsize.c

@@ -0,0 +1,19 @@
+/*	find out if a pointer-sized integer, preferably unsigned,
+	must be declared as an unsigned int or a long
+*/
+
+#include <stdio.h>
+
+main()
+{
+	if (sizeof(unsigned int) == sizeof(char *)) {
+		puts("typedef unsigned int size_type;");
+		return 0;
+	}
+	if (sizeof(long) == sizeof(char *)) {
+		puts("typedef long size_type;");
+		return 0;
+	}
+	fputs(stderr, "funny pointer size\n");
+	return 1;
+}

+ 6 - 0
modules/src/malloc/global.c

@@ -0,0 +1,6 @@
+#include	"param.h"
+#include	"impl.h"
+
+/*	The only global data item:
+*/
+mallink *ml_last;		/* link to the world */

+ 73 - 0
modules/src/malloc/impl.h

@@ -0,0 +1,73 @@
+/*	This file essentially describes how the mallink info block
+	is implemented.
+*/
+
+#define	MIN_SIZE	(1<<LOG_MIN_SIZE)
+#define	MAX_FLIST	(LOG_MAX_SIZE - LOG_MIN_SIZE)
+#if ALIGNMENT != 1 && ALIGNMENT != 2 && ALIGNMENT != 4 && ALIGNMENT != 8 &&\
+    ALIGNMENT != 16
+ALIGNMENT must be a (small) power of two !!!
+#endif
+#define align(n)	(((n) + (ALIGNMENT - 1)) & ~(ALIGNMENT - 1))
+
+union _inf {
+	union _inf *ptr;
+	unsigned int ui;
+};
+
+typedef union _inf mallink;
+#define	MAL_NULL	((mallink *)0)
+
+/*	Access macros; only these macros know where to find values.
+	They are also lvalues.
+*/
+#ifndef	NON_STANDARD
+#define	OFF_SET	0
+#else	def NON_STANDARD
+#define	OFF_SET	2
+#endif	NON_STANDARD
+
+#define	_log_prev_of(ml)	((ml)[-1+OFF_SET]).ptr
+#define	_log_next_of(ml)	((ml)[-2+OFF_SET]).ptr
+#define	_phys_prev_of(ml)	((ml)[-3+OFF_SET]).ptr
+#define	_this_size_of(ml)	((ml)[-4+OFF_SET]).ui
+#ifndef	CHECK
+#define	N_WORDS			4
+#else	ifdef	CHECK
+#define	_checksum_of(ml)	((ml)[-5+OFF_SET]).ui
+#define	_print_of(ml)		((ml)[-6+OFF_SET]).ui
+#define	_mark_of(ml)		((ml)[-7+OFF_SET]).ui
+#define	N_WORDS			7
+#endif	CHECK
+
+#define	mallink_size()		(unsigned int) \
+	align((N_WORDS - OFF_SET) * sizeof (mallink))
+
+#ifdef	CHECK
+#define	set_mark(ml,e)		(_mark_of(ml) = (e))
+#define	mark_of(ml)		(_mark_of(ml))
+
+#define	set_checksum(ml,e)	(_checksum_of(ml) = (e))
+#define	checksum_of(ml)		(_checksum_of(ml))
+#endif	CHECK
+
+
+#define	block_of_mallink(ml)	((char *)ml)
+#define	mallink_of_block(addr)	((mallink *)addr)
+
+#define	public	extern
+#define	publicdata
+#ifndef	EXTERN
+#define	private	static
+#define	privatedata	static
+#else	def	EXTERN
+#define	private	extern
+#define	privatedata
+#endif	EXTERN
+
+#ifdef	ASSERT
+public m_assert();
+#define	assert(b)		(!(b) ? m_assert(__FILE__, __LINE__) : 0)
+#else	ndef	ASSERT
+#define	assert(b)		0
+#endif	ASSERT

+ 128 - 0
modules/src/malloc/log.c

@@ -0,0 +1,128 @@
+#include	"param.h"
+#include	"impl.h"
+#include	"check.h"
+#include	"log.h"
+
+/*	Logical manipulations.
+	The chunks are properly chained in the physical chain.
+*/
+
+privatedata mallink *free_list[MAX_FLIST];
+
+public
+link_free_chunk(ml)
+	register mallink *ml;
+{
+	/*	The free chunk ml is inserted in its proper logical
+		chain.
+	*/
+	register mallink **mlp = &free_list[-1];
+	register unsigned int n = size_of(ml);
+	register mallink *ml1;
+
+	assert(n < (1 << LOG_MAX_SIZE));
+
+	do {
+		n >>= 1;
+		mlp++;
+	}
+	while (n >= MIN_SIZE);
+
+	ml1 = *mlp;
+	set_log_prev(ml, MAL_NULL);
+	set_log_next(ml, ml1);
+	calc_checksum(ml);
+	if (ml1) {
+		/* link backwards
+		*/
+		set_log_prev(ml1, ml);
+		calc_checksum(ml1);
+	}
+	*mlp = ml;
+}
+
+public
+unlink_free_chunk(ml)
+	register mallink *ml;
+{
+	/*	Unlinks a free chunk from (the middle of) the
+		logical chain.
+	*/
+	register mallink *next = log_next_of(ml);
+	register mallink *prev = log_prev_of(ml);
+
+	if (!prev)	{
+		/* it is the first in the chain */
+		register mallink **mlp = &free_list[-1];
+		register unsigned int n = size_of(ml);
+
+		assert(n < (1 << LOG_MAX_SIZE));
+		do {
+			n >>= 1;
+			mlp++;
+		}
+		while (n >= MIN_SIZE);
+		*mlp = next;
+	}
+	else	{
+		set_log_next(prev, next);
+		calc_checksum(prev);
+	}
+	if (next) {
+		set_log_prev(next, prev);
+		calc_checksum(next);
+	}
+}
+
+public mallink *
+search_free_list(class, n)
+	unsigned int n;
+{
+	/*	Searches the free_list[class] for a chunk of at least size n;
+		since it is searching a slightly undersized list,
+		such a block may not be there.
+	*/
+	register mallink *ml;
+	
+	for (ml = free_list[class]; ml; ml = log_next_of(ml))
+		if (size_of(ml) >= n)
+			return ml;
+	return MAL_NULL;		/* nothing found */
+}
+
+public mallink *
+first_present(class)
+	int class;
+{
+	/*	Find the index i in free_list[] such that:
+			i >= class && free_list[i] != MAL_NULL.
+		Return MAL_NULL if no such i exists;
+		Otherwise, return the first block of this list, after
+		unlinking it.
+	*/
+	register mallink **mlp, *ml;
+
+	for (mlp = &free_list[class]; mlp < &free_list[MAX_FLIST]; mlp++) {
+		if ((ml = *mlp) != MAL_NULL)	{
+	
+			*mlp = log_next_of(ml);	/* may be MAL_NULL */
+			if (*mlp) {
+				/* unhook backward link
+				*/
+				set_log_prev(*mlp, MAL_NULL);
+				calc_checksum(*mlp);
+			}
+			return ml;
+		}
+	}
+	return MAL_NULL;
+}
+
+#ifdef	CHECK
+public mallink *
+free_list_entry(i)	{
+	/*	To allow maldump.c access to log.c's private data.
+	*/
+	return free_list[i];
+}
+#endif	CHECK

+ 12 - 0
modules/src/malloc/log.h

@@ -0,0 +1,12 @@
+/*	Algorithms to manipulate the doubly-linked lists of free
+	chunks.
+*/
+
+public link_free_chunk(), unlink_free_chunk();
+public mallink *first_present(), *search_free_list();
+
+#define	set_log_prev(ml,e)	(_log_prev_of(ml) = (e))
+#define	log_prev_of(ml)		(mallink *) (_log_prev_of(ml))
+
+#define	set_log_next(ml,e)	(_log_next_of(ml) = (e))
+#define	log_next_of(ml)		(mallink *) (_log_next_of(ml))

+ 293 - 0
modules/src/malloc/mal.c

@@ -0,0 +1,293 @@
+#include	"param.h"
+#include	"impl.h"
+#include	"check.h"
+#include	"log.h"
+#include	"phys.h"
+
+/*	Malloc space is traversed by N doubly-linked lists of chunks, each
+	containing a couple of house-keeping data addressed as a
+	'mallink' and a piece of useful space, called the block.
+	The N lists are accessed through their starting pointers in
+	free_list[].  Free_list[n] points to a list of chunks between
+	2**(n+LOG_MIN_SIZE) and 2**(n+LOG_MIN_SIZE+1)-1, which means
+	that the smallest chunk is 2**LOG_MIN_SIZE (== MIN_SIZE).
+*/
+
+#ifdef SYSTEM
+#include	<system.h>
+#define SBRK	sys_break
+#else
+#define SBRK	sbrk
+#define	ILL_BREAK		(char *)(-1)	/* funny failure value */
+#endif
+extern char *SBRK();
+#ifdef STORE
+#define	MAX_STORE	32
+private do_free(), sell_out();
+privatedata mallink *store[MAX_STORE];
+#endif STORE
+
+char *
+malloc(n)
+	register unsigned int n;
+{check_mallinks("malloc entry");{
+	register mallink *ml;
+	register int min_class;
+
+	if (n < MIN_SIZE) n = align(MIN_SIZE); else n = align(n);
+#ifdef STORE
+	if (n <= MAX_STORE*MIN_SIZE)	{
+		/* look in the store first */
+		register mallink **stp = &store[(n >> LOG_MIN_SIZE) - 1];
+		
+		if (ml = *stp)	{
+			*stp = log_next_of(ml);
+			check_mallinks("malloc fast exit");
+			return block_of_mallink(ml);
+		}
+	}
+#endif STORE
+
+	check_work_empty("malloc, entry");
+
+	/*	Acquire a chunk of at least size n if at all possible;
+		Try everything.
+	*/
+	{
+		/*	Inline substitution of "smallest".
+		*/
+		register unsigned int n1 = n;
+
+		assert(n1 < (1 << LOG_MAX_SIZE));
+		min_class = 0;
+
+		while (n1 >= MIN_SIZE) {
+			n1 >>= 1;
+			min_class++;
+		}
+	}
+
+	if (min_class >= MAX_FLIST)
+		return (char *) 0;	/* we don't deal in blocks that big */
+	ml = first_present(min_class);
+	if (ml == MAL_NULL)	{
+		/*	Try and extend */
+		register char *p;
+#define	GRABSIZE	4096		/* Power of 2 */
+		register unsigned int req =
+			(n+mallink_size()+GRABSIZE-1)&~(GRABSIZE-1);
+	
+		if (!ml_last)	{
+			/* first align SBRK() */
+		
+			p = SBRK(0);
+			SBRK((int) (align((size_type) p) - (size_type) p));
+		}
+
+		p = SBRK((int)req);
+		if (p == ILL_BREAK)	{
+			/*	Now this is bad.  The system will not give us
+				more memory.  We can only liquidate our store
+				and hope it helps.
+			*/
+#ifdef STORE
+			sell_out();
+			ml = first_present(min_class);
+			if (ml == MAL_NULL)	{
+#endif STORE
+				/* In this emergency we try to locate a suitable
+				   chunk in the free_list just below the safe
+				   one; some of these chunks may fit the job.
+				*/
+				ml = search_free_list(min_class - 1, n);
+				if (!ml)	/* really out of space */
+					return (char *) 0;
+				started_working_on(ml);
+				unlink_free_chunk(ml);
+				check_mallinks("suitable_chunk, forced");
+#ifdef STORE
+			}
+			else started_working_on(ml);
+#endif STORE
+		}
+		else {
+			ml = create_chunk(p, req);
+		}
+		check_mallinks("suitable_chunk, extended");
+	}
+	else started_working_on(ml);
+
+	/* we have a chunk */
+	set_free(ml, 0);
+	calc_checksum(ml);
+	check_mallinks("suitable_chunk, removed");
+	n += mallink_size();
+	if (n + MIN_SIZE <= size_of(ml)) {
+		truncate(ml, n);
+	}
+	stopped_working_on(ml);
+	check_mallinks("malloc exit");
+	check_work_empty("malloc exit");
+	return block_of_mallink(ml);
+}}
+
+free(addr)
+	char *addr;
+{check_mallinks("free entry");{
+	register mallink *ml = mallink_of_block(addr);
+#ifdef STORE
+
+	if (free_of(ml))
+		return;				/* user frees free block */
+	if (size_of(ml) <= MAX_STORE*MIN_SIZE)	{
+		/* return to store */
+		mallink **stp = &store[(size_of(ml) >> LOG_MIN_SIZE) - 1];
+		
+		set_log_next(ml, *stp);
+		*stp = ml;
+		check_mallinks("free fast exit");
+	}
+	else	{
+		do_free(ml);
+		check_mallinks("free exit");
+	}
+}}
+
+private
+do_free(ml)
+	register mallink *ml;
+{{
+#endif
+
+#ifndef STORE
+	if (free_of(ml))	return;
+#endif STORE
+	started_working_on(ml);
+	set_free(ml, 1);
+	calc_checksum(ml);
+	if (! last_mallink(ml)) {
+		register mallink *next = phys_next_of(ml);
+
+		if (free_of(next)) coalesce_forw(ml, next);
+	}
+
+	if (! first_mallink(ml)) {
+		register mallink *prev = phys_prev_of(ml);
+
+		if (free_of(prev)) {
+			coalesce_backw(ml, prev);
+			ml = prev;
+		}
+	}
+	link_free_chunk(ml);
+	stopped_working_on(ml);
+	check_work_empty("free");
+
+	/* Compile-time checks on param.h */
+	switch (0)	{
+	case MIN_SIZE < OFF_SET * sizeof(mallink):	break;
+	case 1:	break;
+	/*	If this statement does not compile due to duplicate case
+		entry, the minimum size block cannot hold the links for
+		the free blocks.  Either raise LOG_MIN_SIZE or switch
+		off NON_STANDARD.
+	*/
+	}
+	switch(0)	{
+	case sizeof(char *) != sizeof(size_type):	break;
+	case 1:	break;
+	/*	If this statement does not compile due to duplicate
+		case entry, size_type is not defined correctly.
+		Redefine and compile again.
+	*/
+	}
+}}
+
+char *
+realloc(addr, n)
+	char *addr;
+	register unsigned int n;
+{check_mallinks("realloc entry");{
+	register mallink *ml = mallink_of_block(addr), *ph_next;
+	register unsigned int size;
+
+	if (n < MIN_SIZE) n = align(MIN_SIZE); else n = align(n);
+	if (free_of(ml)) {
+		unlink_free_chunk(ml);
+		set_free(ml, 0);		/* user reallocs free block */
+	}
+	started_working_on(ml);
+	size = size_of(ml);
+	if (	/* we can simplify the problem by adding the next chunk: */
+		n > size &&
+		!last_mallink(ml) &&
+		(ph_next = phys_next_of(ml), free_of(ph_next)) &&
+		n <= size + mallink_size() + size_of(ph_next)
+	)	{
+		/* add in the physically next chunk */
+		unlink_free_chunk(ph_next);
+		combine_chunks(ml, ph_next);
+		size = size_of(ml);
+		check_mallinks("realloc, combining");
+	}
+	if (n > size)	{		/* this didn't help */
+		char *new;
+		register char *l1, *l2 = addr;
+
+		stopped_working_on(ml);
+		if (!(new = l1 = malloc(n))) return (char *) 0;	/* no way */
+		while (size--) *l1++ = *l2++;
+		free(addr);
+		check_work_empty("mv_realloc");
+		return new;
+	}
+	/* it helped, but maybe too well */
+	n += mallink_size();
+	if (n + MIN_SIZE <= size_of(ml)) {
+		truncate(ml, n);
+	}
+	stopped_working_on(ml);
+	check_mallinks("realloc exit");
+	check_work_empty("realloc");
+	return addr;
+}}
+
+/*	Auxiliary routines */
+
+#ifdef STORE
+private
+sell_out()	{
+	/*	Frees all block in store.
+	*/
+	register mallink **stp;
+	
+	for (stp = &store[0]; stp < &store[MAX_STORE]; stp++)	{
+		register mallink *ml = *stp;
+		
+		while (ml)	{
+			*stp = log_next_of(ml);
+			do_free(ml);
+			ml = *stp;
+		}
+	}
+
+}
+#endif STORE
+
+#ifdef	ASSERT
+public
+m_assert(fn, ln)
+	char *fn;
+{
+	char ch;
+	
+	while (*fn)
+		write(2, fn++, 1);
+	write(2, ": malloc assert failed in line ", 31);
+	ch = (ln / 100) + '0'; write(2, &ch, 1); ln %= 100;
+	ch = (ln / 10) + '0'; write(2, &ch, 1); ln %= 10;
+	ch = (ln / 1) + '0'; write(2, &ch, 1);
+	write(2, "\n", 1);
+	maldump(1);
+}
+#endif	ASSERT

+ 51 - 0
modules/src/malloc/param.h

@@ -0,0 +1,51 @@
+#include	"size_type.h"
+
+/*#	define	NON_STANDARD	/*	If defined, the contents of a block
+					will NOT be left undisturbed after it
+					is freed, as opposed to what it says
+					in the manual (malloc(2)).
+					Setting this option reduces the memory
+					overhead considerably.  I personally
+					consider the specified behaviour an
+					artefact of the original
+					implementation.
+				*/
+
+/*#	define	ASSERT		/*	If defined, some inexpensive tests
+					will be made to ensure the
+					correctness of some sensitive data.
+					It often turns an uncontrolled crash
+					into a controlled one.
+				*/
+
+/*#	define	CHECK		/*	If defined, extensive and expensive
+					tests will be done, inculding a
+					checksum on the mallinks (chunk
+					information blocks).  The resulting
+					information will be printed on a file
+					called mal.out .
+					Additionally a function
+						maldump(n) int n;
+					will be defined, which will dump
+					pertinent info in pseudo-readable
+					form; it aborts afterwards if n != 0.
+				*/
+
+/*#	define	EXTERN		/*	If defined, all static names will
+					become extern, which is a help in
+					using adb(1) or prof(1)
+				*/
+
+#	define	STORE		/*	If defined, separate free lists will
+					be kept of chunks with small sizes,
+					to speed things up a little.
+				*/
+
+#	define SYSTEM		/*	If defined, the system module is used.
+					Otherwise, "sbrk" is called directly.
+				*/
+
+#define	ALIGNMENT	8	
+				/* alignment common to all types */
+#define	LOG_MIN_SIZE	3
+#define	LOG_MAX_SIZE	24

+ 93 - 0
modules/src/malloc/phys.c

@@ -0,0 +1,93 @@
+#include	"param.h"
+#include	"impl.h"
+#include	"check.h"
+#include	"phys.h"
+
+/*	Physical manipulations.
+	The blocks concerned are not in any logical chain.
+*/
+
+public mallink *
+create_chunk(p, n)
+	char *p;
+	unsigned int n;
+{
+	/*	The newly acquired piece of memory at p, of length n,
+		is turned into a free chunk, properly chained in the
+		physical chain.
+		The address of the chunk is returned.
+	*/
+	register mallink *ml;
+	/*	All of malloc memory is followed by a virtual chunk, the
+		mallink of which starts mallink_size() bytes past the last
+		byte in memory.
+		Its use is prevented by testing for ml == ml_last first.
+	*/
+	register mallink *last = ml_last;
+	
+	assert(!last || p == (char *)phys_next_of(last) - mallink_size());
+	ml = (mallink *)(p + mallink_size());	/* bump ml */
+	started_working_on(ml);
+	set_free(ml, 1);
+	set_phys_prev(ml, last);
+	ml_last = ml;
+
+	set_phys_next(ml, (mallink *)((char *)ml + n));
+	calc_checksum(ml);
+	assert(size_of(ml) + mallink_size() == n);
+	if (last && free_of(last)) {
+		coalesce_backw(ml, last);
+		ml = last;
+	}
+	check_mallinks("create_chunk, phys. linked");
+	return ml;
+}
+
+public
+truncate(ml, size)
+	register mallink *ml;
+	unsigned int size;
+{
+	/*	The chunk ml is truncated.
+		The chunk at ml is split in two.
+		The remaining part is then freed.
+	*/
+	register mallink *new = (mallink *)((char *)ml + size);
+	register mallink *ph_next = phys_next_of(ml);
+
+	set_free(new, 1);
+	set_phys_prev(new, ml);
+	set_phys_next(new, ph_next);
+	calc_checksum(new);
+	if (! last_mallink(ml))	{
+		set_phys_prev(ph_next, new);
+		calc_checksum(ph_next);
+		if (free_of(ph_next)) coalesce_forw(new, ph_next);
+	}
+	else	ml_last = new;
+	set_phys_next(ml, new);
+	calc_checksum(ml);
+
+	started_working_on(new);
+	link_free_chunk(new);
+	stopped_working_on(new);
+	check_mallinks("truncate");
+}
+
+public
+combine_chunks(ml1, ml2)
+	register mallink *ml1, *ml2;
+{
+	/*	The chunks ml1 and ml2 are combined.
+	*/
+	register mallink *ml3 = phys_next_of(ml2);
+
+	set_phys_next(ml1, ml3);
+	calc_checksum(ml1);
+	if (!last_mallink(ml2))	{
+		set_phys_prev(ml3, ml1);
+		calc_checksum(ml3);
+	}
+	if (ml_last == ml2)
+		ml_last = ml1;
+}

+ 61 - 0
modules/src/malloc/phys.h

@@ -0,0 +1,61 @@
+/*	Algorithms to manipulate the doubly-linked list of physical
+	chunks.
+*/
+publicdata mallink *ml_last;
+
+#define	__free_of(ml)		((size_type)_phys_prev_of(ml) & 01)
+#define __phys_prev_of(ml)	(mallink *)((size_type)_phys_prev_of(ml) & ~01)
+#define prev_size_of(ml)	((char *)(ml) - \
+				 (char *)__phys_prev_of(ml) - \
+				 mallink_size() \
+				)
+#define	set_phys_prev(ml,e) \
+	(_phys_prev_of(ml) = (mallink *) ((char *)e + __free_of(ml)))
+
+#ifdef	CHECK
+public Error();
+#define	phys_prev_of(ml)	(mallink *) \
+	(first_mallink(ml) ? \
+		(char *)Error("phys_prev_of first_mallink %ld", "somewhere", ml) : \
+		(char *)__phys_prev_of(ml) \
+	)
+#else	ndef	CHECK
+#define	phys_prev_of(ml)	__phys_prev_of(ml)
+#endif	CHECK
+
+#define	first_mallink(ml)	(int) (__phys_prev_of(ml) == 0)
+#define	last_mallink(ml)	(int) ((ml) == ml_last)
+
+/*	There is an ambiguity in the semantics of phys_next_of: sometimes
+	one wants it to return MAL_NULL if there is no next chunk, at
+	other times one wants the address of the virtual chunk at the
+	end of memory.  The present version returns the address of the
+	(virtual) chunk and relies on the user to test last_mallink(ml)
+	first.
+*/
+#define size_of(ml)		(_this_size_of(ml) - mallink_size())
+#define	set_phys_next(ml,e) \
+	(_this_size_of(ml) = (unsigned int)((char *)(e) - (char *)(ml)))
+#define	phys_next_of(ml)	(mallink *) ((char *)(ml) + _this_size_of(ml))
+
+#define	set_free(ml,e) \
+	(_phys_prev_of(ml) = (mallink *) \
+		((e) ? (size_type) _phys_prev_of(ml) | 01 : \
+		       (size_type) _phys_prev_of(ml) & ~01))
+#define	free_of(ml)		(__free_of(ml))
+
+#define coalesce_forw(ml,nxt)	( unlink_free_chunk(nxt), \
+				  combine_chunks((ml), (nxt)))
+
+#define coalesce_backw(ml,prv)	( unlink_free_chunk(prv), \
+				  stopped_working_on(ml), \
+				  combine_chunks((prv), (ml)), \
+				  started_working_on(prv))
+
+#ifdef	CHECK
+#define	set_print(ml,e)		(_print_of(ml) = (e))
+#define	print_of(ml)		(_print_of(ml))
+#endif	CHECK
+
+public truncate(), combine_chunks();
+public mallink *create_chunk();