Page 189

Write a routine bfree(p,n) that will free an arbitrary block p of n characters into the free list maintained by malloc and free. By using bfree, a user can add a static or external array to the free list at any time.

/* I found this SO answer very helpful in understanding some of the routines presented:
 * https://stackoverflow.com/questions/13159564/explain-this-implementation-of-malloc-from-the-kr-book/36512105#36512105 */

#include <unistd.h>

typedef long Align;

union header {
	struct {
		union header *ptr;
		unsigned size;
	} s;
	Align _;
};

typedef union header Header;

static Header base;
static Header *freep = NULL;
static Header *knrmorecore(unsigned nu);

void *knrmalloc(unsigned nbytes)
{
	Header *currp, *prevp;
	Header *morecore(unsigned);
	unsigned nunits;

	if (nbytes == 0) {
		return NULL;
	}

	nunits = (nbytes + sizeof(Header) - 1) / sizeof(Header) + 1;

	if ((prevp = freep) == NULL) {
		base.s.ptr = freep = prevp = &base;
		base.s.size = 0;
	}

	for (currp = prevp->s.ptr;; prevp = currp, currp = currp->s.ptr) {
		if (currp->s.size >= nunits) {
			if (currp->s.size == nunits) {
				prevp->s.ptr = currp->s.ptr;
			} else { /* allocate tail end */
				currp->s.size -= nunits;
				currp += currp->s.size;
				currp->s.size = nunits;
			}
			freep = prevp;
			return (void *)(currp + 1);
		}
		if (currp == freep) {
			if ((currp = knrmorecore(nunits)) == NULL) {
				return NULL;
			}
		}
	}
}

#define NALLOC 1024
void knrfree(void *ap);
static Header *knrmorecore(unsigned nu)
{
	char *cp;
	Header *up;

	if (nu < NALLOC) {
		nu = NALLOC;
	}
	cp = sbrk(nu * sizeof(Header));
	if (cp == (char *)-1) {
		return NULL;
	}
	up = (Header *)cp;
	up->s.size = nu;
	knrfree((void *)(up + 1));
	return freep;
}

void knrfree(void *ap)
{
	Header *insertp, *currp;

	if (ap == NULL) {
		return;
	}

	insertp = (Header *)ap - 1;

	if (insertp->s.size == 0) {
		return;
	}

	for (currp = freep; !(insertp > currp && insertp < currp->s.ptr);
	     currp = currp->s.ptr) {
		if (currp >= currp->s.ptr &&
		    (insertp > currp || insertp < currp->s.ptr)) {
			break;
		}
	}

	if (insertp + insertp->s.size == currp->s.ptr) { /* join to upper nbr */
		insertp->s.size += currp->s.ptr->s.size;
		insertp->s.ptr = currp->s.ptr->s.ptr;
	} else {
		insertp->s.ptr = currp->s.ptr;
	}

	if (currp + currp->s.size == insertp) {
		currp->s.size += insertp->s.size;
		currp->s.ptr = insertp->s.ptr;
	} else {
		currp->s.ptr = insertp;
	}
	freep = currp;
}

#include <string.h>
void *mycalloc(int n, size_t size)
{
	void *res = knrmalloc(n * size);
	if (res != NULL) {
		memset(res, 0, n * size);
	}
	return res;
}

void *bfree(void *p, size_t n)
{
	if (n <= sizeof(Header)) {
		return NULL;
	}

	Header *hp = (Header *)p;
	hp->s.size = (n / sizeof(Header)) - 1;

	knrfree(p + 1);

	return p + 1;
}

#include <stdio.h>
#include <stdlib.h>
int main()
{
	char str[] =
		"sizeof(Header) appears to be 16 bytes on my system (makes sense, as it is the size of a long)"
		"so this string needs to be at least 17 characters long else there isn't enough space for the"
		"block header.";

	void *res = bfree(str, sizeof(str));

	if (res == NULL) {
		puts("error: bfree failed");
		exit(1);
	}

	puts("success");
	exit(0);
}