/*************************************************************************/
/*                                                                       */
/*                  Language Technologies Institute                      */
/*                     Carnegie Mellon University                        */
/*                        Copyright (c) 1999                             */
/*                        All Rights Reserved.                           */
/*                                                                       */
/*  Permission is hereby granted, free of charge, to use and distribute  */
/*  this software and its documentation without restriction, including   */
/*  without limitation the rights to use, copy, modify, merge, publish,  */
/*  distribute, sublicense, and/or sell copies of this work, and to      */
/*  permit persons to whom this work is furnished to do so, subject to   */
/*  the following conditions:                                            */
/*   1. The code must retain the above copyright notice, this list of    */
/*      conditions and the following disclaimer.                         */
/*   2. Any modifications must be clearly marked as such.                */
/*   3. Original authors' names are not deleted.                         */
/*   4. The authors' names are not used to endorse or promote products   */
/*      derived from this software without specific prior written        */
/*      permission.                                                      */
/*                                                                       */
/*  CARNEGIE MELLON UNIVERSITY AND THE CONTRIBUTORS TO THIS WORK         */
/*  DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING      */
/*  ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT   */
/*  SHALL CARNEGIE MELLON UNIVERSITY NOR THE CONTRIBUTORS BE LIABLE      */
/*  FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES    */
/*  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN   */
/*  AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,          */
/*  ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF       */
/*  THIS SOFTWARE.                                                       */
/*                                                                       */
/*************************************************************************/
/*             Author:  Alan W Black (awb@cs.cmu.edu)                    */
/*               Date:  July 1999                                        */
/*************************************************************************/
/*                                                                       */
/*  Basic wraparounds for malloc and free                                */
/*                                                                       */
/*************************************************************************/
#include "cst_file.h"
#include "cst_alloc.h"
#include "cst_error.h"

#ifdef UNDER_CE
#include <windows.h>
#endif /* UNDER_CE */

/* define this if you want to trace memory usage */
/* #define CST_DEBUG_MALLOC */
/* #define CST_DEBUG_MALLOC_TRACE */
#ifdef CST_DEBUG_MALLOC
int cst_allocated = 0;
int cst_freed = 0;
int cst_alloc_max = 0;
int cst_alloc_imax = 0;
int cst_alloc_num_calls = 0;
int cst_alloc_out = 0;
#ifdef CST_DEBUG_MALLOC_TRACE
/* This is a crude memory tracer to find leaks */
int cst_alloc_ckpt = -1;
/* You have to know how big this should be to use it, its for debuging only */
#define NUM_CHUNKS 10000000
void *cst_alloc_cunks[NUM_CHUNKS];
#endif
#endif

#ifndef CST_USER_MALLOC	/* Define to override cst_safe_alloc, cst_safe_calloc, cst_safe_realloc, cst_free */

void *cst_safe_alloc(int size)
{
    /* returns pointer to memory all set 0 */
    void *p = NULL;
    if (size < 0)
    {
	cst_errmsg("alloc: asked for negative size %d\n", size);
	cst_error();
    }
    else if (size == 0)  /* some mallocs return NULL for this */
	size++;

#ifdef CST_DEBUG_MALLOC
    if (size > cst_alloc_imax)
    {
	cst_alloc_imax = size;
    }
    cst_allocated += size;
    cst_alloc_out += size;
    size += 2 * sizeof(int);
#endif

#ifdef UNDER_CE
    p = (void *)LocalAlloc(LPTR, size);
#else
    p = (void *)calloc(size,1);
#endif

#ifdef CST_DEBUG_MALLOC
#ifdef CST_DEBUG_MALLOC_TRACE
    if (cst_alloc_num_calls == cst_alloc_ckpt)
	cst_dbgmsg("cst_malloc: %d\n", cst_alloc_ckpt);
    cst_alloc_cunks[cst_alloc_num_calls] = p;
#endif
    cst_alloc_num_calls++;
    *(int *)p = 1314;
    p = (int *)p + 1;
    *(int *)p = size - (2 * sizeof(int));
    if ((cst_allocated - cst_freed) > cst_alloc_max)
	cst_alloc_max = cst_allocated - cst_freed;
    p = (int *)p + 1;
#endif

    if (p == NULL)
    {
	cst_errmsg("alloc: can't alloc %d bytes\n", size);
	cst_error();
    }

    return p;
}

void *cst_safe_calloc(int size)
{
    return cst_safe_alloc(size);
}

void *cst_safe_realloc(void *p,int size)
{
    void *np=0;

#ifdef CST_DEBUG_MALLOC
    cst_free(p);
    return cst_safe_alloc(size);
#endif

    if (size == 0)
	size++;  /* as some mallocs do strange things with 0 */

    if (p == NULL)
	np = cst_safe_alloc(size);
    else
#ifdef UNDER_CE
	np = LocalReAlloc((HLOCAL)p, size, LMEM_MOVEABLE|LMEM_ZEROINIT);
#else
	np = realloc(p,size);
#endif

    if (np == NULL)
    {
	cst_errmsg("CST_REALLOC failed for %d bytes\n",size);
	cst_error();
    }

    return np;
}

void cst_free(void *p)
{
    if (p != NULL)
    {
#ifdef CST_DEBUG_MALLOC
	if (*((int *)p - 2) != 1314)
	{
	    cst_dbgmsg("CST_MALLOC_DEBUG freeing non-malloc memory\n");
	    return;
	}
	if (*((int *)p - 1) <= 0)
	{
	    cst_dbgmsg("CST_MALLOC_DEBUG re-freeing memory\n");
	    return;
	}
	cst_freed += *((int *)p - 1);
	cst_alloc_out -= *((int *)p - 1);
	*((int *)p - 1) = 0; /* mark it as freed */
	p = (int *)p - 2;
#endif
#ifndef CST_DEBUG_MALLOC_TRACE
#ifdef UNDER_CE
	if (LocalFree(p) != NULL)
	{
	    cst_errmsg("LocalFree(%p) failed with code %x\n",
		       p, GetLastError());
	    cst_error();
	}
#else
	free(p);
#endif
#endif
    }
}

#endif

#ifdef CST_DEBUG_MALLOC_TRACE

void cst_find_unfreed()
{
    int i, t;

    
    for (i = 0, t = 0;
	 i < NUM_CHUNKS
	     && i < cst_alloc_num_calls
	     && cst_alloc_cunks[i];
	 i++)
    {
	if (((int *)cst_alloc_cunks[i])[1] != 0) 
	{
            cst_dbgmsg("unfreed at %d\n", i);
	    t++;
	}
    }
    cst_dbgmsg("total unfreed %d\n", t);
}

void cst_alloc_debug_summary()
{
    cst_find_unfreed();
    printf("allocated %d freed %d max %d imax %d calls %d out %d\n",
	   cst_allocated, cst_freed, cst_alloc_max, 
	   cst_alloc_imax, cst_alloc_num_calls, cst_alloc_out);
}
#endif

#ifdef UNDER_CE
cst_alloc_context new_alloc_context(int size)
{
    HANDLE h;

    h = HeapCreate(0, size, 0);
    return (cst_alloc_context) h;
}

void delete_alloc_context(cst_alloc_context ctx)
{
    HANDLE h;

    h = (HANDLE)ctx;
    HeapDestroy(h);
}

void *cst_local_alloc(cst_alloc_context ctx, int size)
{
    HANDLE h;

    h = (HANDLE)ctx;
    if (h)
	return HeapAlloc(h, HEAP_ZERO_MEMORY, size);
    else
	return LocalAlloc(LPTR, size);
}

void cst_local_free(cst_alloc_context ctx, void *p)
{
    HANDLE h;

    h = (HANDLE)ctx;
    if (h)
	HeapFree(h, 0, p);
    else
	LocalFree(p);
}
#endif


