/* $Copyright:	$
 * Copyright (c) 1984, 1985, 1986, 1987, 1988, 1989, 1990 
 * Sequent Computer Systems, Inc.   All rights reserved.
 *  
 * This software is furnished under a license and may be used
 * only in accordance with the terms of that license and with the
 * inclusion of the above copyright notice.   This software may not
 * be provided or otherwise made available to, or used by, any
 * other person.  No title to or ownership of the software is
 * hereby transferred.
 */

#ident	"$Header: align_malloc.c 1.2 89/08/07 $"

/*
 * align_malloc.c
 *
 *	Routines like malloc()/free() which align the returned
 *	buffer per a caller supplied rounding value:
 *
 *	_align_malloc(nbytes, round):  
 *		like malloc(nbytes) but returned value aligned on a
 *		"round" bytes boundary.  "round" must be a multiple
 *		of sizeof(char *).
 *
 *	_align_free(p):
 *		like free(p) but for buffers returned via _align_malloc().
 *
 *	_align16_malloc(bytes):
 *		like malloc(nbytes) but returned value aligned on a
 *		16 byte boundary.  All raw zdc disk transfer buffers
 *		must be 16 byte aligned.
 */

/* $Log:	align_malloc.c,v $
 */

/*LINTLIBRARY*/

#define ROUND 16    /* raw zdc disk transfer buffers must be 16 byte aligned */
#define roundup(x, r)	((((int)(x))+(r)-1)/(r)*(r))

#define NULL 0


char *
_align_malloc(nbytes, round)
	unsigned nbytes;
	unsigned round;
{
	char *p, *q;
	unsigned nb;

	if (round == 0)
		round = ROUND;

	/*
	 * Must align with (char *).
	 */
	round = roundup(round, sizeof(char *));

	/*
	 * Increase requested size to hold real malloc pointer
	 * and to allow for alignment.
	 */
	nb = nbytes + sizeof(char *) + (round - 1);

	p = (char *)malloc(nb);
	if (p == NULL)
		return((char *)NULL);
	/*
	 * Find first aligned buffer address beyond space to hold 
	 * real malloc pointer.  Save real malloc pointer immediately
	 * before this.
	 */
	q = (char *)roundup(p + sizeof(char *), round);
	*((char **)q - 1) = p;

	return(q);
}


void
_align_free(q)
	register char *q;
{
	char *p;

	/*
	 * Retrieve real malloc pointer and free it.
	 */
	p = *((char **)q - 1);
	free(p);
}


char *
_align16_malloc(nbytes)
	unsigned nbytes;
{
	return(_align_malloc(nbytes, 16));
}


#ifdef LOCAL_DEBUG

main(argc, argv)
	char **argv;
{
	int size  = atoi(argv[1]);
	int round = atoi(argv[2]);
	int count = atoi(argv[3]);
	char *p;
	int i;

	for (i = 0; i < count; i++) {
		p = _align_malloc(size, round);

		printf("_align_malloc(%d, %d) = 0x%x\n", size, round, (int)p);
		if ((int)p % round)
			printf("*** UNALIGNED ***\n");
		/* _align_free(p); */
	}
}
#endif /* LOCAL_DEBUG */
