/*
 * memory.c:    malloc(), free(), realloc() and calloc()
 */

/*
 *      Edition History
 *
 *  1993/08     V.1.0       kmatsui
 *      Released on NIFTY-Serve / FC and FOS9.
 *  1996/09     V.2.0       kmatsui
 *      Absorbed DEBUG mode into normal mode and implemented new _MEM_DEBUG
 *          mode.
 *      Implemented several ways of error checking and error handling.
 *      Added checking of erroneous writing on non-allocated area.
 *      Implemented filename and linenumber information.
 *      Debugged implementation for large data memory model on MS-DOS.
 *  1997/03     V.2.1       kmatsui
 *      Implemented calling-count information.
 *  1998/05     V.2.2       kmatsui
 *      Added implementation for Win32.
 *      Environmental variables are read at initialization on _MEM_DEBUG mode.
 *      Added the mode of malloc(0) returning a zero-sized block.
 *  1998/11     V.2.3       kmatsui
 *      Made last & large free block returned to OS.
 *      Made realloc( old_p, 0) returns a pointer to 0-sized block when
 *          _MALLOC0_NULL == FALSE.
 *      Moved call of set_memfp() to listheap.c.
 *  2003/06     V.2.4       kmatsui
 *      Fixed a bug on returning last & large free block.
 *  2003/09     V.2.5       kmatsui
 *      Implemented _msize() for Visual C.
 *  2006/01     V.2.5.1     kmatsui
 *      Fixed a bug of missing _FMAGIC on free block in some cases
 *          -- fixed by freebeans.
 *  2007/07     V.2.5.2     kmatsui
 *      Fixed a bug on returning last & large free block in case of the header
 *      crosses _NALLOC boundary.
 *  2007/08     V.2.5.3     kmatsui
 *      Fixed again the bug on returning last & large free block.
 */

#if defined __WIN32__ || defined WIN32 || defined _WIN32
#define     WIN32API    1
#endif

#include    <stdio.h>
#include    <string.h>
#include    <errno.h>

#if WIN32API
#include    <windows.h>
#include	<assert.h>
#endif
#include    "yalloc.h"

#undef      malloc
#undef      free
#undef      realloc
#undef      calloc

#define     TRUE    1
#define     FALSE   0

#if ! WIN32API
#if _PROTO
    extern int  brk( void _HUGE *);
#else
    extern int  brk();
#endif
#endif

#if _PROTO

static char     *_malloc( size_t, int);
static int      morecore( size_t);
static int      chk_alloc( register _Header _HUGE *);
static void     insert_block( _Header _HUGE *, int);
static void     mem_error( int);
#if _RETURN_SIZE
static _ptr_t   lesscore( char _HUGE *);
#endif
#if _MEM_DEBUG
extern int      list_heap( int);
extern int      set_memcheck( void);
extern unsigned set_memlist( void);
extern void     (*set_merrfunc( void))( int);
#endif
#if _MSC_VER
       size_t   _msize( void *);
#endif

#else

static char     *_malloc();
static int      morecore();
static int      chk_alloc();
static void     insert_block();
static void     mem_error();
#if _RETURN_SIZE
static _ptr_t   lesscore();
#endif
#if _MEM_DEBUG
extern int      list_heap();
extern int      set_memcheck();
extern unsigned set_memlist();
extern void     (*set_merrfunc())();
#endif

#endif

// int     memcheck = 0;                   /* mode of error checking   */
#ifdef DEBUG
//int     memcheck = MERR_FIRSTFIT | MERR_CHKALL;                   /* mode of error checking   */
int     memcheck = MERR_FIRSTFIT;                   /* mode of error checking   */
#else
int     memcheck = MERR_FIRSTFIT;                   /* mode of error checking   */
#endif


#if _PROTO
    void    (*merrfunc)( int) = exit;   /* function to be executed on error */
#else
    void    (*merrfunc)() = exit;
#endif
    unsigned    memlist;        /* when `memcheck' is to be set to MERR_LIST*/
#if _MEM_DEBUG
    unsigned    _memcount;      /* number of calls to malloc(), free(), etc.*/
    int     _memline;           /* __LINE__ from which malloc() is called   */
    static char     unknown[] = "(unknown)";
    char    *_memfile = unknown;        /* __FILE__ which called malloc()   */
    _Header _base = { &_base, 0, 0, NULL, (_ptr_t) sizeof (_Header), };
#else
    _Header _base = { &_base, (_ptr_t) sizeof (_Header), };/* to get started*/
#endif
    static _Header _HUGE    *basep;         /* pointer to _base     */

#if _NO_GLOBAL
    char    *_heapbase = NULL;
    char    *_brklvl = NULL;
#else
    extern char     *_heapbase;
#if defined __TURBOC__ && ! (defined __WIN32__ || defined WIN32)
    extern _ptr_t   _brklvl;
#else
    extern char     *_brklvl;
#endif
#endif

#if WIN32API
	/** クリティカルセクション **/
	static LPCRITICAL_SECTION g_critical_section = NULL;
	static CRITICAL_SECTION g_critical_section_body;
#endif

/* malloc:  general-purpose storage allocator   */
void    *malloc( size)
    size_t  size;
    /*
     * Don't change the value of this argument. The startup of TC and BC
     * small model expects malloc() to retain the value. (sigh..)
     */
{

	char	*cp;

#if WIN32API
	if (g_critical_section == NULL) {
		// 最初の１回目の呼び出しはスレッドセーフではない
		g_critical_section = &g_critical_section_body;
		InitializeCriticalSection(g_critical_section);
	}
	EnterCriticalSection(g_critical_section);
#endif

#if _MEM_DEBUG
    _memcount++;
#if _GETENV
    if (_memcount == _GETENV) {
        /* environs have been probably initialized by startup       */
        /* set variables to control error checking from environs    */
        set_memcheck();
        set_merrfunc();
        set_memlist();
    }
#endif
    if (_memcount == memlist && memlist)
        memcheck |= MERR_LIST;
#endif
    cp = _malloc( size, TRUE);
#if _MEM_DEBUG
    _memline = 0;
    _memfile = unknown;
#endif

#if WIN32API
	LeaveCriticalSection(g_critical_section);
#endif

    return cp;
}

static char     *_malloc( size, check)
    size_t  size;
    int     check;
{
#if _PROTO
#if ! WIN32API
#if defined LSI_C || defined __LSIC__
    extern void     *sbrk( unsigned);
#else
    extern void     *sbrk( int);
#endif
#endif
#else
    extern char     *sbrk();
#endif
#if _MEM_DEBUG
    int     err;
#endif
    size_t  usize;                          /* size rounded up      */
    _Header     *prev_fit = NULL;       /* previous block to fitted block   */
    register _Header _HUGE  *p;
    register _Header _HUGE  *prevp;         /* previous block to p  */

    if (basep == NULL) {
#if _NO_GLOBAL || ! __MWC09__
#if WIN32API
        DWORD   reserve;
        for (reserve = 0x10000000; _heapbase == NULL && reserve > _NALLOC;
                reserve >>= 1)
            _heapbase = (char *) VirtualAlloc( NULL, reserve, MEM_RESERVE,
                    PAGE_NOACCESS);
            /* reserve contiguous address space of 256MB at maximum */
#else
        _heapbase = (char _HUGE *) sbrk( 0);
#endif
        if (_heapbase == NULL)                  /* no heap at all   */
            return NULL;
        _heapbase = (char *)(((_ptr_t)(_heapbase + _ODD)) & ~(_ptr_t)_ODD);
                                                /* ensure alignment */
#if defined __TURBOC__ && ! (defined __WIN32__ || defined WIN32)
        _brklvl = (_ptr_t)_heapbase;
#else
        _brklvl = _heapbase;
#endif
#endif
        basep = &_base;
    }
#if _MEM_DEBUG
    else {
        if (check && (memcheck & (MERR_CHKALL | MERR_LIST))
                && (err = list_heap( memcheck & MERR_LIST)) != 0
                && err != -1) {
            mem_error( err);
            if (err != ETRAILWRT && err != EFREEWRT)
                return NULL;
        }
    }
#endif

#if _MALLOC0_NULL
    if (size == 0)
        return NULL;
#endif
    if ((usize = (size + _CELLSIZE + _ODD) & ~_ODD) < size)
        return NULL;                                /* wrap round   */
                                    /* else round up to unit sizes  */
    prevp = basep;
    if ((p = prevp->_next) != prevp) {      /* there are free blocks*/
        /* Search linked list of free blocks    */
        for ( ; ; prevp = p, p = p->_next) {
#if _MEM_DEBUG
            if ((memcheck & (MERR_CHKALL | MERR_LIST)) == 0)
#endif
            {
                if (
#if ! _MALLOC0_NULL
                    p->_size > _CELLSIZE &&
#endif
                        (*((char *)p + _CELLSIZE) & 0xFF) != _FMAGIC)
                    mem_error( EFREEWRT);   /* free area has been written on*/
            }
            if (usize <= p->_size) {                /* big enough   */
                if (memcheck & MERR_FIRSTFIT) {
                    prev_fit = (_Header *)prevp;    /* first fit    */
                    break;
                }
                if (prev_fit == NULL || p->_size < prev_fit->_next->_size)
                    prev_fit = (_Header *)prevp;    /* better fit   */
            }
            if (p->_next == basep)          /* wrapped around list  */
                break;      /* 'p' and 'prevp' should be preserved  */
#if _MEM_DEBUG
            if ((memcheck & (MERR_CHKALL | MERR_LIST)) == 0)
#endif
            {
                if (p->_next <= p) {
                    mem_error( EFREEBLK);
                    return NULL;
                }
            }
        }
    }
    if (prev_fit) {                     /* there's a fitted block   */
        prevp = prev_fit;
    } else {                                /* need to get memory   */
        size_t  rsize = usize;          /* size to request to morecore()    */
        int     last_is_free = FALSE;   /* last block is free block ?       */

        if ((char _HUGE *)p + p->_size == (char _HUGE *)_brklvl) {
            last_is_free = TRUE;        /* the last block is free   */
            rsize = usize - p->_size;   /* new memory really in need*/
        }
        if (morecore( rsize) == FALSE)
            return NULL;                            /* no core left */
        if (last_is_free == FALSE)
            prevp = p;                  /* the old last free block  */
    }
    p = prevp->_next;                       /* best fitted block    */
    if (p->_size <= usize + _CELLSIZE + sizeof (_unit_t)) {
                                                /* barely enough    */
        prevp->_next = p->_next;                /* use entire block */
    } else {                /* more than enough, free excess memory */
        _Header _HUGE   *freep;

        freep = prevp->_next = (_Header *)((char _HUGE *)p + usize);
        freep->_next = p->_next;
        freep->_size = p->_size - usize;
#if _MEM_DEBUG
        freep->_count = _memcount;
        freep->_line = _memline;
        freep->_file = _memfile;
#endif
        *((char *)freep + _CELLSIZE) = _FMAGIC; /* set watcher      */
        p->_size = usize;                   /* allocate first half  */
    }
    {   /* Note:    Allocated blocks does not make linked list      */
        size_t  gsize;                  /* size of trailing garbabe */

        if ((gsize = p->_size - size - _CELLSIZE) != 0) /* length of garbage*/
            *((char *)p + size + _CELLSIZE) = _FMAGIC;  /* boundary */
        p->_next = (_Header *)(~(_ptr_t)gsize);     /* magic for garbage    */
    }
#if _MEM_DEBUG
    p->_count = _memcount;  /* record the calling count of malloc(), etc.   */
    p->_line = _memline;    /* record the line-number which called malloc() */
    p->_file = _memfile;    /* record the filename which called malloc()    */
#endif
    return (char *)p + _CELLSIZE;       /* return the usable area   */
    /* don't normalize the pointer to assure _CELLSIZE <= offset    */
}

#if defined _LARGE
#define FAR_LIMIT   (char *)0x9FF00000UL
#endif
#if defined _LARGE && _MSC_VER
#define PTR_DIFF( ptr1, ptr2)   ((long)(ptr1 - ptr2))
#else
#define PTR_DIFF( ptr1, ptr2)   (ptr1 - ptr2)
#endif

/* morecore:    ask system for more memory  */
static int  morecore( size)
    size_t  size;
{
#if defined _CORELEFT
    extern unsigned     _CORELEFT( void);
    char _HUGE  *heap_limit;
#elif   defined _FARCORELEFT
    extern unsigned long    _FARCORELEFT( void);
#endif

    register char _HUGE     *cp;                    /* old heap top */
    char _HUGE  *brk_p;                             /* new heap top */

    cp = (char *)_brklvl;
#if defined _CORELEFT
#if defined __BORLANDC__
    heap_limit = cp + _CORELEFT();
#else
    heap_limit = cp + _CORELEFT() - 0x100;  /* assure to be away from stack */
#endif
    if ((brk_p = cp + size) < cp || heap_limit < brk_p)     /* crash stack  */
#else
    if ((brk_p = cp + size) < cp)                   /* wrap round   */
#endif
        return FALSE;
    brk_p = ((char *)                       /* round up to pages    */
            (((_ptr_t)brk_p + _NALLOC - 1) & ~ (_ptr_t)(_NALLOC - 1)));

#if defined _CORELEFT
    if (brk_p < cp || heap_limit < brk_p)
        brk_p = heap_limit;                         /* maximum heap */
#elif   defined _SMALL
    if (brk_p < cp)
        brk_p = (char *)~_ODD;              /* not to wrap round    */
#elif   defined _LARGE
    if (FAR_LIMIT < brk_p) {        /* limit of conventional memory */
#if defined _FARCORELEFT
        unsigned long   core = _FARCORELEFT();
        if (size <= core)
            brk_p = cp + core;
#else
        if (cp + size <= FAR_LIMIT)
            brk_p = FAR_LIMIT;          /* safe even if DPMI is set */
#endif
        else
            return FALSE;
    }
#else
    if (brk_p < cp)                                 /* wrap round   */
        return FALSE;
#endif

#if WIN32API
    if (VirtualAlloc( cp, brk_p - cp, MEM_COMMIT, PAGE_READWRITE) == NULL)
#else
    if (brk( brk_p) == -1)
#endif
    {                                       /* no space at all      */
#if _NOMEM
        errno = _NOMEM;             /* set errno brk() hasn't set   */
#endif
        return FALSE;
    }

#if _NO_GLOBAL
    _brklvl = brk_p;
#endif
    ((_Header *)cp)->_size = PTR_DIFF( brk_p, cp);  /* size gotten  */
    insert_block( (_Header *)cp, TRUE);     /* place into the list  */
    return TRUE;                            /* error never occurs   */
}

/* free:    put block 'ap' in free list */
void    free( ap)
    void    *ap;
{
    register _Header _HUGE  *bp;

#if WIN32API
	if (g_critical_section == NULL) {
		// 最初の１回目の呼び出しはスレッドセーフではない
		g_critical_section = &g_critical_section_body;
		InitializeCriticalSection(g_critical_section);
	}
	EnterCriticalSection(g_critical_section);
#endif

#if _MEM_DEBUG
    if (++_memcount == memlist && memlist)
        memcheck |= MERR_LIST;
#endif
    if (ap == NULL)
        goto f_ret;                                 /* do nothing   */
    bp = (_Header *)((char _HUGE *)ap - _CELLSIZE);     /* point to header  */

    if (chk_alloc( bp) == 0)
        insert_block( bp, FALSE);

f_ret:
#if _MEM_DEBUG
    _memline = 0;
    _memfile = unknown;
#endif

#if WIN32API
    LeaveCriticalSection(g_critical_section);
#endif

	return;
}

static int  chk_alloc( bp)
    register _Header _HUGE  *bp;
{
#if _MEM_DEBUG
    int     err;
#endif

    if (! _ALLOCATED( bp->_next)) {             /* illegal header   */
        mem_error( EFREEP);
        return -1;
    }
#if _MEM_DEBUG
    if (memcheck & (MERR_CHKALL | MERR_LIST)) {
        if ((err = list_heap( memcheck & MERR_LIST)) != 0 && err != -1) {
            mem_error( err);
            if (err != EFREEWRT && err != ETRAILWRT)
                return -1;
        }
    } else
#endif
    {
        if (bp->_next != (_Header *)_PMAX
                && (*((char *)bp + bp->_size - ~(_ptr_t)bp->_next) & 0xFF)
                    != _FMAGIC)
            mem_error( ETRAILWRT);  /* boundary of user area is written over*/
    }
    return 0;
}

static void     insert_block( bp, fromcore)
    _Header _HUGE   *bp;
    int     fromcore;
{
    register _Header _HUGE  *p, *prev_p;

    prev_p = basep;                 /* the previous free block to p */
    p = basep->_next;               /* the first free block         */
    if (p != p->_next) {            /* there are free blocks        */
        if (bp < p) {               /* freed cell at atart of list  */
            p = basep;
        } else {
            for ( ; p->_next <= bp || bp <= p; prev_p = p, p = p->_next) {
#if _MEM_DEBUG
                if ((memcheck & (MERR_CHKALL | MERR_LIST)) == 0)
#endif
                    if (
#if ! _MALLOC0_NULL
                        p->_size > _CELLSIZE &&
#endif
                            (*((char *)p + _CELLSIZE) & 0xFF) != _FMAGIC)
                        mem_error( EFREEWRT);
                if (p->_next == basep)  /* freed cell at end of list*/
                    break;
#if _MEM_DEBUG
                if ((memcheck & (MERR_CHKALL | MERR_LIST)) == 0)
#endif
                {
                    if (p->_next <= p) {
                        mem_error( EFREEBLK);
                        return;
                    }
                }
            }
        }
    }
    if ((char _HUGE *)bp + bp->_size == (char _HUGE *)p->_next) {
        bp->_size += p->_next->_size;   /* join to upper neighbor   */
        bp->_next = p->_next->_next;
    } else {
        bp->_next = p->_next;
    }
    if ((char _HUGE *)p + p->_size == (char _HUGE *)bp) {
        p->_size += bp->_size;          /* join to lower neighbor   */
        p->_next = bp->_next;
        bp = p;
        p = prev_p;             /* the free block previous to bp    */
    } else {
        p->_next = bp;
    }
#if ! _MALLOC0_NULL
    if (bp->_size > _CELLSIZE)
#endif
        *((char *)bp + _CELLSIZE) = _FMAGIC;        /* set watcher  */
#if _RETURN_SIZE
    if ((!fromcore && bp->_size >= _RETURN_SIZE)
            && (char _HUGE *)bp + bp->_size == (char _HUGE *)_brklvl) {
        _ptr_t      residue;
        /* return the last & large free block to O.S.   */
        if ((residue = lesscore((char *)bp)) > _CELLSIZE) {
            bp->_size = residue;    /* the residual small free block*/
        } else {            /* returned the block entirely to O.S.  */
            p->_next = basep;
            if (residue) {                  /* too small fragment   */
                size_t  gsize;

                while (p < bp) {    /* search last allocated block  */
                    prev_p = p;
                    p = (_Header *)((char _HUGE *)p + p->_size);
                }
                prev_p->_size += residue;   /* add to last block    */
                gsize = ~(_ptr_t)prev_p->_next;
                gsize += residue;
                prev_p->_next = (_Header *)(~(_ptr_t)gsize);    /* update   */
                *((char *)prev_p + prev_p->_size - gsize) = _FMAGIC;
            }
            return;
        }
    }
#endif
#if _MEM_DEBUG
    bp->_count = _memcount;
    bp->_line = _memline;
    bp->_file = _memfile;
#endif
}

#if _RETURN_SIZE
/* lesscore:    return memory to system */
static _ptr_t   lesscore( bp)
    char _HUGE  *bp;
{
    char _HUGE  *brk_p;                             /* new heap top */

    brk_p = ((char *)                       /* round up to pages    */
            (((_ptr_t)bp + _NALLOC - 1) & ~ (_ptr_t)(_NALLOC - 1)));

#if WIN32API
    VirtualFree( brk_p, _brklvl - brk_p, MEM_DECOMMIT);
#else
    brk( brk_p);
#endif

#if _NO_GLOBAL
    _brklvl = brk_p;
#endif
    return brk_p - bp;
}
#endif

/* Handle heap error    */
static void     mem_error( err)
    int     err;
{
    errno = err;

#ifdef _WIN32_WCE
	{
		_TCHAR buff[256];
		_TCHAR format[] = _T("Heap error occurred. errno=%d");
		_stprintf(buff, format, errno);
		MessageBox(NULL, buff, _T("Mysaifu JVM heap error"), MB_OK | MB_ICONERROR);
		assert(0);

		// 例外を発生させる
		RaiseException(EXCEPTION_IN_KMMALLOC, EXCEPTION_NONCONTINUABLE, 0, NULL);

	}
#endif

    if (merrfunc)
        (*merrfunc)( err);  /* execute the user specified function  */

}

void    *realloc( old_p, size)
    register void   *old_p;
    size_t  size;
{
    int     err;
    _Header _HUGE   *old_header;
    size_t  old_size;
    size_t  copy_size;
    char    *new_p;

#if WIN32API
	if (g_critical_section == NULL) {
		// 最初の１回目の呼び出しはスレッドセーフではない
		g_critical_section = &g_critical_section_body;
		InitializeCriticalSection(g_critical_section);
	}
	EnterCriticalSection(g_critical_section);
#endif

#if _MEM_DEBUG
    if (++_memcount == memlist && memlist)
        memcheck |= MERR_LIST;
#endif
    if (old_p) {
        old_header = (_Header *)((char _HUGE *)old_p - _CELLSIZE);
        if (chk_alloc( old_header) == -1) {
            new_p = NULL;
            goto r_ret;
        }
#if _MALLOC0_NULL
        if (size == 0) {
            insert_block( old_header, FALSE);
            new_p = NULL;
            goto r_ret;
        }
#endif
        old_size = old_header->_size - _CELLSIZE;
        if (size <= old_size
                && old_size <= ((size + _CELLSIZE + _ODD) & ~_ODD)) {
            size_t  gsize;

            if ((gsize = old_size - size) != 0)
                *((char *)old_p + size) = _FMAGIC;  /* new boundary */
            old_header->_next
                    = (_Header *)(~(_ptr_t)gsize);  /* magic        */
#if _MEM_DEBUG
            old_header->_count = _memcount;
            old_header->_line = _memline;
            old_header->_file = _memfile;
#endif
            new_p = old_p;                      /* don't reallocate */
            goto r_ret;
        }
        copy_size = (old_size <= size) ? old_size : size;
    } else {
#if _MALLOC0_NULL
        if (size == 0) {
            new_p = NULL;
            goto r_ret;
        }
#endif
        old_size = 0;
    }
    err = errno;
    if ((new_p = (char *)_malloc( size, old_p ? FALSE : TRUE)) == NULL) {
        if (size < old_size) {          /* re-sizing does enough ?  */
            _Header _HUGE   *free_p;
            size_t  gsize;
            size_t  new_cell_size;

            errno = err;            /* reset errno set by malloc()  */
            new_cell_size = (size + _CELLSIZE + _ODD) & ~_ODD;
            old_header->_size = new_cell_size;
            if ((gsize = new_cell_size - size - _CELLSIZE) != 0)
                *((char _HUGE *)old_p + size) = _FMAGIC;    /* new boundary */
            old_header->_next = (_Header _HUGE *)(~(_ptr_t)gsize);
#if _MEM_DEBUG
            old_header->_count = _memcount;
            old_header->_line = _memline;
            old_header->_file = _memfile;
#endif
            free_p = (_Header *)((char *)old_header + new_cell_size);
            free_p->_size = old_size - new_cell_size + _CELLSIZE;
            insert_block( free_p, FALSE);   /* free excess space    */
            new_p = old_p;                  /* error never occurs   */
        }
    } else if (old_p) {
        memcpy( new_p, old_p, copy_size);
        insert_block( old_header, FALSE);
    }
r_ret:
#if _MEM_DEBUG
    _memline = 0;
    _memfile = unknown;
#endif

#if WIN32API
	LeaveCriticalSection(g_critical_section);
#endif

    return new_p;
}

void    *calloc( nmemb, size)
    size_t  nmemb;
    size_t  size;
{
    size_t  n;
    char    *ptr;

    n = nmemb * size;
    ptr = (char *)malloc( n);
    if (ptr)
        memset( ptr, '\0', n);
    return ptr;
}

#if _MSC_VER    /* For Visual C startup routine */
size_t  _msize( ap)
    void *  ap;
{
    if (ap == NULL)
        return  0;
    return  ((_Header *)((char _HUGE *)ap - _CELLSIZE))->_size;
}
#endif

