/* %Z%%M% %I% %E% */

/*
 * Copyright (c) 1990, 1991, 1995 by Wayne C. Gramlich.
 * All rights reserved.
 *
 * Permission to use, copy, modify, distribute, and sell this software
 * for any purpose is hereby granted without fee provided that the above
 * copyright notice and this permission are retained.  The author makes
 * no representations about the suitability of this software for any purpose.
 * It is provided "as is" without express or implied warranty.
 */

/* Implementation of a heap memory allocator: */

#ifndef ERROR_EXPORTS_H
#include "error_exports.h"
#endif

#ifndef FILE_EXPORTS_H
#include "file_exports.h"
#endif

#ifndef HEAP_DEFS_H
#include "heap_defs.h"
#endif

#ifndef LIBC_EXPORTS_H
#include "libc_exports.h"
#endif

#ifndef PORT_H
#include "port.h"
#endif

#ifndef UNIX_MALLOC_H
#include "unix_malloc.h"
#endif

#ifndef UNIX_MEMORY_H
#include "unix_memory.h"
#endif

/* LINTLIBRARY */

/*
 *	heap_alloc(heap, size)
 *		This routine will return a pointer to storage of at least
 *		"size" bytes allocated from "heap".  The pointer will be
 *		aligned to sizeof(Pointer).
 */
Pointer
heap_alloc(
	Heap		heap,
	int		size)
{
	Pointer		pointer;
	static Pointer	bkp = (Pointer)0;

#if LINUX
	if (size < 4) {
		size = 4;
	}
#endif
	pointer = (*heap->alloc)(heap->handle, size);
	if (pointer == bkp) {
		bkp = bkp;		/* Plant breakpoint here */
	}
	return pointer;
}

/*
 *	heap_alloc_zeroed(heap, size)
 *		This routine will return a pointer to storage of at least
 *		"size" bytes allocated from "heap" that has been prefilled
 *		with zeros.  The pointer will be aligned to sizeof(Pointer).
 */
Pointer
heap_alloc_zeroed(
	Heap		heap,
	int		size)
{
	Pointer		pointer;

	pointer = (*heap->alloc)(heap->handle, size);
	(void)memset((Str)pointer, '\0', size);
	return pointer;
}

/*
 * heap_free(heap, pointer)
 *	This routine will release the storage pointed to by "pointer".
 */
void
heap_free(
	Heap		heap,
	Pointer		pointer)
{
	(*heap->free)(heap->handle, pointer);
}

/*
 * heap_realloc(heap, pointer, size)
 *	This routine will return a pointer to storage of at least
 *	"size" bytes allocated from "heap".  If "pointer" can be
 *	safely extended, it will be returned; otherwise, new
 *	storage will be allocated, the contents of "pointer"
 *	will be copied into it, and "pointer" will be freed.
 *	The returned pointer will be aligned to sizeof(Pointer).
 */
Pointer
heap_realloc(
	Heap		heap,
	Pointer		pointer,
	int		size)
{
	pointer = (*heap->realloc)(heap->handle, pointer, size);
	return pointer;
}

/*
 * heap_create(handle, alloc, free, realloc)
 *	This routine will create and return a heap object containing "alloc",
 *	"free", "handle", and "realloc".  The object is allocated using
 *	a call to "alloc(handle, sizeof(struct Heap))".
 */
Heap
heap_create(
	Heap_handle		handle,
	Heap_pointer_routine	alloc, /* (Heap_handle, int size */
	Heap_void_routine	free, /* (Heap_handle, Pointer */
	Heap_pointer_routine	realloc) /* (Heap_Handle, Pointer, int size)*/
{
	Heap			heap;

	heap = (Heap) (*alloc)(handle, sizeof( *((Heap)0) ));
	heap->alloc = alloc;
	heap->free = free;
	heap->realloc = realloc;
	heap->handle = handle;
	return heap;
}

/*
 * heap_standard_create()
 *	This routine will return the standard heap object using libc's malloc
 *	and free.
 */
Heap
heap_standard_create(PORT_NO_ARGS)
{
	static Heap	heap = (Heap)0;

	if (heap == (Heap)0) {
		heap = heap_create((Heap_handle)0,
				   heap_standard_alloc,
				   heap_standard_free,
				   heap_standard_realloc);
	}
	return heap;
}

/*
 * heap_standard_alloc(handle, size)
 *	This routine will allocate "size" bytes using malloc ignoring
 *	"handle".
 */
/* ARGSUSED */
Pointer
heap_standard_alloc(
	Heap_handle	handle,
	int		size)
{
	Pointer		pointer;

	if (size < 0) {
		error_fatal("heap_alloc(%d) size is negative", size);
	}
	pointer = (Pointer)malloc((unsigned)size);
	if (pointer == (Pointer)0) {
		error_fatal( /* TCOVOK */
		    "Could not allocate %d bytes (%s)\n", /*TCOVOK */
		    size, "out of swap space?"); /* TCOVOK */
	}
	return pointer;
}

/*
 * heap_standard_free(handle, pointer)
 *	This will free the storage pointed to by "pointer" using the libc
 *	free() routine.  The "handle" argument is ignord.
 */
/* ARGSUSED */
void
heap_standard_free(
	Heap_handle	handle,
	Pointer		pointer)
{
	free((Str)pointer);
}

/*
 * heap_standard_realloc(handle, pointer, size)
 *	This will allocate and return "size" bytes of storage.  If possible,
 *	the bytes pointed to by "pointer" will be reused.  Otherwise, new
 *	storage will be allocated and the contents of "pointer" copied.
 *	The "handle" argument is ignored.
 */
/* ARGSUSED */
Pointer
heap_standard_realloc(
	Heap_handle	handle,
	Pointer		pointer,
	int		size)
{
	if (size < 0) {
		error_fatal("Negative size %d passed to realloc()\n", size);
	}
	return (Pointer)realloc((Str)pointer, (unsigned)size);
}

/*
 * heap_free_disallowed(handle, size)
 *	This will cause a fatal error to occur.
 */
void
heap_free_disallowed(
	Heap_handle	handle,
	Pointer		pointer)
{
	error_fatal("heap_free(0x%x, 0x%x) disallowed.\n", handle, pointer);
}

/*
 * heap_realloc_disallowed(handle, pointer, size)
 *	This will cause a fatal error to occur.
 */
Pointer
heap_realloc_disallowed(
	Heap_handle	handle,
	Pointer		pointer,
	int		size)
{
	error_fatal("heap_realloc(0x%x, 0x%x, %d) disallowed.\n",
		    handle, pointer, size);
	/* NORETURN */
	return (Pointer)0;
}

/*
 * heap_size_alloc(heap_size, size)
 *	This routine will allocate "size" bytes using "heap_size" and store
 *	a "size" along with the returned object.
 */
/* ARGSUSED */
Pointer
heap_size_alloc(
	Heap_size	heap_size,
	int		size)
{
	Pointer		pointer;

	pointer = heap_alloc(heap_size->heap, size + sizeof(int));
	*((int *)pointer) = size;
	heap_size->size += size;
	return (Pointer)((int)pointer + sizeof(int));
}

/*
 * heap_size_create(heap)
 */
Heap
heap_size_create(
	Heap		heap)
{
	Heap_size	heap_size;

	heap_size = heap_allocate(heap, Heap_size);
	heap_size->heap = heap;
	heap =  heap_create((Pointer)heap_size,
			    heap_size_alloc,
			    heap_size_free,
			    heap_size_realloc);
	heap_size->size = 0;
	return heap;
}

/*
 * heap_size_free(heap_size, pointer)
 *	This routine will free the storage pointed to by "pointer" using
 *	"heap_size".
 */
/* ARGSUSED */
void
heap_size_free(
	Heap_size	heap_size,
	Pointer		pointer)
{
	pointer = (Pointer)((int)pointer -  sizeof(int));
	heap_size->size -= *((int *)pointer);
	heap_free(heap_size->heap, pointer);
}

/*
 * heap_size_get(heap)
 *	This will return the current number of bytes allocated in "heap".
 */
int
heap_size_get(
	Heap		heap)
{
	return ((Heap_size)heap->handle)->size;
}

/*
 * heap_size_realloc(heap_size, pointer, size)
 *	This will allocate and return "size" bytes of storage.  If possible,
 *	the bytes pointed to by "pointer" will be reused.  Otherwise, new
 *	storage will be allocated and the contents of "pointer" copied.
 *	A size is kept with "heap_size".
 */
/* ARGSUSED */
Pointer
heap_size_realloc(
	Heap_size	heap_size,
	Pointer		pointer,
	int		size)
{
	pointer = (Pointer)((int)pointer -  sizeof(int));
	heap_size->size -= *((int *)pointer);
	heap_size->size += size;
	pointer = heap_realloc(heap_size->heap, pointer, size + sizeof(int));
	*((int *)pointer) = size;
	return (Pointer)((int)pointer + sizeof(int));
}

