/* @(#)malloc.c	1.1 */
#include "sys/param.h"
#include "sys/types.h"
#include "sys/systm.h"
#include "sys/map.h"
#include "sys/var.h"
#include "sys/scat.h"

/*
 * 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 swap map unit is 512 bytes.
 * Algorithm is first-fit.
 */
malloc(mp, size)
register struct map *mp;
{
	int n;

	if (mp != coremap)
		return(domall(mp, size));
	do
		if ((n = domall(mp, size)) != 0)
			return(n);
	while (xmrelse() != 0);
	return(0);
}
domall(mp, size)
struct map *mp;
register size;
{
	register struct map *bp;
	register a;

	for (bp = mapstart(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);
				mapsize(mp)++;
			}
			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 a;
{
	register struct map *bp;
	register t;

	bp = mapstart(mp);
	for (; bp->m_addr<=a && bp->m_size!=0; bp++);
	if (bp>mapstart(mp) && (bp-1)->m_addr+(bp-1)->m_size == a) {
		(bp-1)->m_size += size;
		if (a+size == bp->m_addr) {
			(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;
			}
			mapsize(mp)++;
		}
	} else {
		if (a+size == bp->m_addr && bp->m_size) {
			bp->m_addr -= size;
			bp->m_size += size;
		} else if (size) {
			if (mapsize(mp) == 0) {
				printf("\nDANGER: mfree map overflow at map 0x%x\n", mp);
				printf("  lost %d items starting at 0x%x\n", size, a);
				return;
			}
			do {
				t = bp->m_addr;
				bp->m_addr = a;
				a = t;
				t = bp->m_size;
				bp->m_size = size;
				bp++;
			} while (size = t);
			mapsize(mp)--;
		}
	}
#ifdef NONSCATLOAD
	/*
	 * Wake scheduler when freeing core
	 */
	if (mp==coremap) {
		if(runin) {
			runin = 0;
			wakeup((caddr_t)&runin);
		}
	} else
#else
	if (mp != coremap)
#endif
	/*
	 * wakeup anyone waiting for a map
	 */
	if (mapwant(mp)) {
		mapwant(mp) = 0;
		wakeup((caddr_t)mp);
	}
}

/*
 * return the largest size in the given map structure
 */
mallocl(mp)
struct map *mp;
{
	register struct map *bp;
	register a;

	a = 0;
	for (bp=mapstart(mp); bp->m_size; bp++)
		if (bp->m_size > a)
			a = bp->m_size;
	return(a);
}

#ifdef NONSCATLOAD
/*
 * malloc size clicks starting at address 'start'
 */
mallocat(mp, size, start)
struct map *mp;
register start;
{
	register struct map *bp;
	register a;

	for (bp=mapstart(mp); bp->m_size; bp++) {
		if (bp->m_addr == start && 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);
				mapsize(mp)++;
			}
			return(a);
		}
	}
	return(0);
}
#else

extern int nscatfree;

/*
 * allocate size units from the memory map
 */
memalloc(size)
{
	int n;

	while ((n = domemalloc(size)) == NULL)
		if (xmrelse() == 0)
			break;
	return(n);
}

domemalloc(size)
register size;
{
	register struct scatter *s;
	register short i;
	register a1, a2;

	if (size <= 0) {
		printf("memalloc error: tried to allocate %d units\n", size);
		return(NULL);
	}
	if (size > nscatfree) {
		/* printf("memalloc: less than %d free pages at present\n",
			size); */
		return(NULL);
	}
	s = scatmap;
	a1 = a2 = scatfreelist.sc_index;
	if (size > 1) {
		i = size - 2;
		do {
			a1 = s[a1].sc_index;
		} while (--i != -1);
	}
	scatfreelist.sc_index = s[a1].sc_index;
	s[a1].sc_index = SCATEND;
	nscatfree -= size;
	/* printf("memalloc: found %d free pages starting at index %d\n",
		size, a2); */
	return(a2);
}

/*
 * allocate size contiguous units from the memory map
 */
cmemalloc(size)
{
	int n;

	do
		scatsort();
	while ((n = docmemalloc(size))==NULL && xmrelse());
/* printf("cmemalloc:size=%d starting address=0x%x\n", size, n); */
	return(n);
}
docmemalloc(size)
register size;
{
	register struct scatter *s, *ss;
	register short i;
	register a1, a2, n;

	if (size <= 0) {
		printf("cmemalloc error: tried to allocate %d units\n", size);
		return(NULL);
	}
	if (size > nscatfree)
		return(NULL);
	s = scatmap;
	ss = &scatfreelist;
	a1 = ss->sc_index;
	for (;;) {
		n = memcontig(a1, size);
		if (n >= size)
			break;
		if (n > 1) {
			i = n - 2;
			do
				a1 = s[a1].sc_index;
			while (--i != -1);
		}
		ss = &s[a1];
		if (ss->sc_index == SCATEND)
			return(NULL);
		a1 = ss->sc_index;
	}
	a2 = a1;
	if (size > 1) {
		i = size - 2;
		do
			a1 = s[a1].sc_index;
		while (--i != -1);
	}
	ss->sc_index = s[a1].sc_index;
	s[a1].sc_index = SCATEND;
	nscatfree -= size;
	/* printf("cmemalloc: found %d free pages starting at index %d\n",
		size, a2); */
	if (countscat(a2) != size)
		printf("cmemalloc:improper allocation countscat=%d size=%d\n",
			countscat(a2), size);
	return(a2);
}

/*
 * free memory map chain
 */
memfree(a)
register a;
{
	register struct scatter *s;
	register i, a1, a2;

	if (a <= 0 || a >= v.v_nscatload) {
		printf("memfree:illegal index %d (0x%x)\n", a, a);
		return;
	}
	i = 1;
	s = scatmap;
	a1 = a;
	while ((a2 = s[a1].sc_index) != SCATEND) {
		a1 = a2;
		i++;
	}
	/* printf("memfree:%d units freed starting at %d\n", i, a); */
	s[a1].sc_index = scatfreelist.sc_index;
	scatfreelist.sc_index = a;
	nscatfree += i;
	/*
	 * Wake scheduler when freeing memory
	 */
	if (runin) {
		runin = 0;
		wakeup((caddr_t)&runin);
	}
}

/*
 * find number of contiguous memory pages
 */
memcontig(sindex, ct)
register sindex, ct;
{
	register struct scatter *s;
	register a1, n;

	if (sindex == SCATEND || ct <= 0) {
		printf("memcontig:sindex=0x%x ct=%d\n", sindex, ct);
		return(0);
	}
	n = 1;
	s = scatmap;
	a1 = ixtoc(sindex);
	while (--ct > 0) {
		if ((sindex = s[sindex].sc_index) == SCATEND)
			break;
		if (++a1 != ixtoc(sindex))
			break;
		n++;
	}
	return(n);
}

/*
 * sort the scatter load map
 */
scatsort()
{
	register struct scatter *s, *sf;
	register int j, k, n, *ip, *jp;
	register short i;

	/*
	 * clear scatter sort array
	 */
	ip = scsortmap;
	i = ((v.v_nscatload+31) >> 5) - 1;
	do
		*ip++ = 0;
	while (--i != -1);
	/*
	 * build bit map of free pages
	 */
	s = scatmap;
	sf = &scatfreelist;
	ip = scsortmap;
	for (j = sf->sc_index; j != SCATEND; j = s[j].sc_index)
		ip[j>>5] |= 1 << (j&31);
	/*
	 * rebuild freelist
	 */
	n = 0;
	sf->sc_index = SCATEND;
	j = ((v.v_nscatload+31) >> 5) - 1;
	jp = &scsortmap[j];
	for ( ; j >= 0; j--, jp--) {
		if (*jp == 0)
			continue;
		for (k=31; k>=0; k--) {
			if (*jp&(1<<k)) {
				n++;
				i = sf->sc_index;
				sf->sc_index = (j << 5) + k;
				s[(j << 5) + k].sc_index = i;
			}
		}
	}
	nscatfree = n;
}
#endif
