#include "../h/local.h"

#ifdef  SCCS_ID
static char SCCS_ID [] = "@(#)malloc.c    	3.4	 12:36:47 - 82/02/04 ";
#endif  SCCS_ID

#include "../h/param.h"
#include "../h/systm.h"
#include "../h/map.h"
#ifdef  UCB_METER
#include "../h/vm.h"
#endif  UCB_METER

#ifdef  CHKMAP
#define ppanic(z) printf ("return address = %x\n", retaddr);
#define retad(x) (((int *) &(x))[-1])
#endif  CHKMAP

/*
 * Allocate 'size' units from the given
 * map. Return the base of the allocated
 * space.
 * In a map, the addresses are increasing and the
 * list is terminated by a 0 size.
 * The core map unit is 64 bytes; the swap map unit
 * is 512 bytes.
 * Algorithm is first-fit.
 */
malloc(mp, size)
struct map *mp;
{
	register unsigned int a;
	register struct map *bp;

#ifdef  CHKMAP
	if (mp == swapmap && size == 0)
		printf ("mallocing size 0\n");
	mverify (mp, retad (mp));
#endif  CHKMAP
	for (bp=mp; bp->m_size; bp++) {
		if (bp->m_size >= size) {
			a = bp->m_addr;
			bp->m_addr += size;
			if ((bp->m_size -= size) == 0) {
				do {
					bp++;
					(bp-1)->m_addr = bp->m_addr;
				} while ((bp-1)->m_size = bp->m_size);
			}
#ifdef  UCB_METER
			if (mp == coremap)
				freemem -= size;
#endif  UCB_METER
			return(a);
		}
	}
	return(0);
}

/*
 * Free the previously allocated space aa
 * of size units into the specified map.
 * Sort aa into map and combine on
 * one or both ends if possible.
 */
mfree(mp, size, a)
struct map *mp;
register int a;
{
	register struct map *bp;
	register unsigned int t;
#ifdef  CHKMAP
	int *retaddr;

	retaddr = retad(mp);
	if (mp == swapmap && size == 0)
		printf ("mfreeing size 0\n");
	/* make sure mfree addr not out of range */
	if (mp == swapmap && a + size > nswap + CLSIZE) {
		printf ("mfree: out of range %d %d\n", a, size);
		prmap (mp);
		ppanic ("");
		return;
	}
#endif  CHKMAP
	if ((bp = mp)==coremap && runin) {
		runin = 0;
		wakeup((caddr_t)&runin);	/* Wake scheduler when freeing core */
	}
#ifdef  UCB_METER
	if (mp == coremap)
		freemem += size;
#endif  UCB_METER
#ifdef  CHKMAP
	mverify (mp, retaddr);
#endif  CHKMAP
#ifdef  USTEST
	for (; USTOI(bp->m_addr) <= a && bp->m_size!=0; bp++)
#else   USTEST
	for (; bp->m_addr<=a && bp->m_size!=0; bp++)
#endif  USTEST
		continue;
#ifdef  CHKMAP
	if (bp>mp) {
		register int prevtop;
		prevtop = (bp-1)->m_addr+(bp-1)->m_size;
		if (mp == swapmap && prevtop > a) {
			printf ("mfree: inside already free segment %d %d\n",
				a, size);
			prmap (mp);
			ppanic ("");
			return;
		}
		if (prevtop < a)
			goto insert;
#else   CHKMAP
	if (bp>mp && (bp-1)->m_addr+(bp-1)->m_size == a) {
#endif  CHKMAP
		/* coalesce with a lower entry */
		(bp-1)->m_size += size;
#ifdef  USTEST
		if (a+size == USTOI(bp->m_addr)) {
#else   USTEST
		if (a+size == bp->m_addr) {
#endif  USTEST
			(bp-1)->m_size += bp->m_size;
			while (bp->m_size) {
				bp++;
				(bp-1)->m_addr = bp->m_addr;
				(bp-1)->m_size = bp->m_size;
			}
		}
	} else {
#ifdef  CHKMAP
 insert:
#endif  CHKMAP
#ifdef  USTEST
		if (a+size == USTOI(bp->m_addr) && bp->m_size) {
#else   USTEST
		if (a+size == bp->m_addr && bp->m_size) {
#endif  USTEST
			bp->m_addr -= size;
			bp->m_size += size;
		} else if (size) {
#ifdef  CHKMAP
			if (   mp == swapmap
			    && a + size > USTOI(bp->m_addr)
			    && bp->m_size) {
				printf ("mfree: overlaps %d %d\n", a, size);
				prmap (mp);
				ppanic ("");
				return;
			}
#endif  CHKMAP
			do {
				t = bp->m_addr;
				bp->m_addr = a;
				a = t;
				t = bp->m_size;
				bp->m_size = size;
				bp++;
			} while (size = t);
		}
	}
/* CGL BUG FIX...mfree did not check for map overflow  1jul81 */
	if (bp >= &mp[mp == coremap ? cmapsiz - 1 : smapsiz - 1])
		panic("mfree map not big enough");
/* end */
}

#ifdef  CHKMAP
/*
 * Verify that the map is not all hosed up.
 * In a map, the addresses are increasing and the
 * list is terminated by a 0 size.
 */
mverify (mp, retaddr)
struct map *mp;
int *retaddr;
{
	register struct map *bp;
	register int max;

	if (mp != swapmap)
		return;
	max = nswap + CLSIZE;
	if (mp->m_size)
		for (bp = mp; (&bp[1])->m_size; bp++) {
			if (bp->m_addr + bp->m_size > max) {
				printf ("mverify: entry out of range: %d\n",
					 bp->m_addr);
				prmap (mp);
				ppanic ("");
			}
			else if (bp->m_addr + bp->m_size
				 >= USTOI((&bp[1])->m_addr)) {
				printf ("mverify: overlap: %d\n",
					 bp->m_addr);
				prmap (mp);
				ppanic ("");
			}
		}
	return;
}

/* print out the swapmap */
prmap (map)
struct map *map;
{
	register int tmp;
	register int ltmp;

	for (tmp = 0; tmp < smapsiz && map[tmp].m_size; tmp += 4) {
		for(ltmp = 0; ltmp < 4; ++ltmp)
			printf("%d\t%d%s",
			       map[tmp + ltmp].m_size,
			       map[tmp + ltmp].m_addr, ltmp == 3 ? "" : "\t");
		printf("\n");
	}
	return;
}
#endif  CHKMAP
