/* MacMemory.c
 *
 * This file provides wrappers for memory allocation routines.
 * Most of these could be done with macros, but, we use routines
 * to avoid recompilation.
 *
 * manuel
 */

#include <stdlib.h>
#include <string.h>
#include "MacMemory.h"

//
// Define this to use the Macintosh Memory Manager,
// as opposed to ANSI malloc (and others).
//

#define gUseMemManager 1


void *mcalloc(size_t nelem, size_t nsize)
{
	void *p;

	if (gUseMemManager) {
		long newSize;
		long freeBytes;
		
		newSize = nelem * nsize;
	
		p = NewPtr(newSize);
		if (p == NULL) {
			
			freeBytes = CompactMem(newSize);
			if (freeBytes >= newSize)
				p = NewPtr(newSize);
	
		}
	
		if (p != NULL)
			memset(p, 0, newSize);
	}
	else {
		p = calloc(nelem, nsize);
	}
	return p;
}

void mfree(void *p)
{

	if (gUseMemManager)
		DisposPtr(p);
	else
		free(p);
}

void *mmalloc(size_t n)
{
	void *p;

	if (gUseMemManager) {
		long freeBytes;
	
		p = NewPtr(n);
		if (p == NULL) {
			
			freeBytes = CompactMem(n);
			if (freeBytes >= n)
				p = NewPtr(n);
	
		}
	}
	else
		p = malloc(n);

	return p;
}

void *mrealloc(void *ptr, size_t size)
{
	void *p;

	if (gUseMemManager) {
		long newSize = size;
		long oldSize;
	
	
		if (ptr == NULL)
			p = mmalloc(size);
		else {
			oldSize = GetPtrSize(ptr);
	
			/* assume it will fail */
			p = NULL;
		
			/* New block is smaller: just change the size of the
			 * old pointer and return
			 */
			if (newSize < oldSize) {
				SetPtrSize(ptr, size);
				if (MemError() == noErr)	/* this never fails */
					p = ptr;				/* but just in case */
			}
		
			/* New block is larger: 1) try to change the size of
			 * the old block.  If fail, 2) then allocate new block
			 * and copy old contents over.
			 */
			else if (oldSize < newSize) {
				SetPtrSize(ptr, size);		/* larger size */
				if (MemError() == noErr)
					p = ptr;
		
				else if (MemError() == memFullErr) {
					long bytesFree;
		
					bytesFree = CompactMem(newSize);
					if (bytesFree >= newSize) {
						p = NewPtr(size);
						if (p != NULL) {
							BlockMove(ptr, p, oldSize);
							DisposPtr(ptr);
						}
					}
				}
			}
		
			/* New Block is Same size: do nothing */
			else {
				p = ptr;
			}
		}


	}
	else
		p = realloc (ptr, size);

	return p;
}
