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

    This file is part of libRILc, a standard C library for GCC on Amiga OS.
    Copyright © 1997, 1998  Rask Ingemann Lambertsen

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Library General Public License for more details.

    You should have received a copy of the GNU Library General Public
    License along with this library; if not, write to the Free
    Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

    As a special exception, if you link this library with files compiled
    with a GNU compiler to produce an executable, this does not cause the
    resulting executable to be covered by the GNU General Public License.
    This exception does not however invalidate any other reasons why the
    executable file might be covered by the GNU General Public License.
*/

#include <exec/types.h>
#include <exec/memory.h>
#include <clib/macros.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>

#ifdef __PPC__
#include <powerup/gcclib/powerup_protos.h>
#define AllocPooled(a,b)  PPCAllocPooled(a,b)
#define FreePooled(a,b,c) PPCFreePooled(a,b,c)
#define AddHead(a, b)     PPCAddHead(a, b)
#define Remove(a)         PPCRemove(a)
#else
#include <proto/exec.h>
#endif

#include "memory/globals.h"
#include "internal/systemcalls.h"
#include "internal/globals.h"

/* Warning: Call __syscall_start() _before_ calling this function. */
static VOID __add_mem_node (struct __mem_node *memnode)
{
  AddHead ((struct List *) &__mem_list, (struct Node *) &memnode->node);
}

/* Warning: Call __syscall_start() _before_ calling this function. */
static VOID __remove_mem_node (struct __mem_node *memnode)
{
  Remove ((struct Node *) &memnode->node);
}

void *malloc (size_t size)
{
    struct __mem_node *newmem;
    __syscall_handle sh;

    /* Try to get memory. */
    sh = __syscall_start ();
    if ((newmem = AllocPooled (__mem_pool, (ULONG) size + sizeof (struct __mem_node))))
    {
        /* Initialise and return. */
        newmem->size = size;
        __add_mem_node (newmem);
        __syscall_end (sh);
        return ((void *) (newmem + 1));
    }
    else
    /* No memory. */
    {
        __seterrno ();
        __syscall_end (sh);
        return (NULL);
    }
}

void *calloc (size_t number, size_t size)
{
    void *mem;

    if ((mem = malloc (number * size)))
        bzero (mem, number * size);

    return (mem);
}

void free (void *mem)
{
    __syscall_handle sh;
    struct __mem_node *memb;

    if (mem)
    {
        memb = (struct __mem_node *) mem - 1;
        sh = __syscall_start ();
        __remove_mem_node (memb);
        FreePooled (__mem_pool, (APTR) memb, memb->size + sizeof (struct __mem_node));
        __syscall_end (sh);
    }
}

void *realloc (void *mem, size_t size)
{
    void *newmem;

    /* Handle the special cases first. */
    /* New allocation, plain malloc(). */
    if (mem == NULL)
        return (malloc (size));

    /* No new allocation, plain free(). */
    if (size == 0 && mem != NULL)
    {
        free (mem);
        return (NULL);
    }

    /* New allocation is exactly as big as the old one, do nothing. */
    if (((struct __mem_node *) mem - 1)->size == size)
        return (mem);

    /* OK, try to reallocate and move memory block. */
    if ((newmem = malloc (size)))
    {
        struct __mem_node *oldmemb, *newmemb;

        oldmemb = ((struct __mem_node *) mem) - 1;
        newmemb = ((struct __mem_node *) newmem) - 1;

        memcpy (newmem, mem, MIN (oldmemb->size, newmemb->size));
        free (mem);
        return (newmem);
    }
    else
        return (NULL);
}
