/*
 **    Name: rmarealloc.c
 **
 **    Date: Sun Jan  5 16:32:51 2003
 **
 */

#include "rmamem.h"
#include "osmodule.h"
#include "osheap.h"
#include <string.h>
#include <stdlib.h>
#include "kernel.h"

char *rmarealloc(void *ptr,size_t size)
{
    char *new_ptr,
        *old_ptr;

    int old_size;

    _kernel_swi_regs regs;

    if (rmamem_status == RMAMEM_UNITIALISED)
    {
        rmamem_init(RMAMEM_UNITIALISED);
    }

    if (ptr == NULL)
    {
        return rmamalloc(size);
    }

    switch (rmamem_status)
    {
        case RMAMEM_DYNAMICAREA:

            if (rmamem_grow(size + rmamem_details.add) == 1)
            {
                return NULL;
            }

            regs.r[0] = OSHeap_ReadSize;
            regs.r[1] = rmamem_details.base;
            regs.r[2] = (int)ptr;

            if (_kernel_swi(XOS_Heap,&regs,&regs) == NULL)
            {
                old_size = regs.r[3] - rmamem_details.add;

                regs.r[0] = OSHeap_Realloc;
                regs.r[1] = rmamem_details.base;
                regs.r[2] = (int)ptr;
                regs.r[3] = size - old_size;
                if (_kernel_swi(XOS_Heap,&regs,&regs) == NULL)
                {
                    ptr = (char *)regs.r[2];
                }
                else
                {
                    ptr = NULL;
                }
            }
            else
            {
                ptr = NULL;
            }
            break;

        case RMAMEM_MALLOC:
            ptr = realloc(ptr,size);
            break;

        case RMAMEM_RMA:
            old_ptr = (char *)ptr;

            old_ptr -= 4;

            old_size = *(int *)old_ptr;

            regs.r[0] = OSModule_Realloc;
            regs.r[2] = (int)old_ptr;
            regs.r[3] = size - old_size + 4;

            if (_kernel_swi(XOS_Module,&regs,&regs) == NULL)
            {
                new_ptr = (char *)regs.r[2];
                *(int *)new_ptr = size;
                return new_ptr + 4;
            }
            else
            {
                regs.r[0] = OSModule_Alloc;
                regs.r[3] = size + 4;

                if (_kernel_swi(XOS_Module,&regs,&regs) == NULL)
                {
                    new_ptr = (char *)regs.r[2];
                    if (size > old_size)
                    {
                        memcpy(new_ptr,ptr,old_size);
                    }
                    else
                    {
                        memcpy(new_ptr,ptr,size);
                    }

                    rmafree(old_ptr);
                    *(int *)new_ptr = size;
                    return new_ptr + 4;
                }
                else
                {
                    return NULL;
                }
            }
            break;
    }

    return ptr;
}
