/*
================================ COPYRIGHT ================================
The contents of this file are subject to the VTech Informations Ltd. License
of VT-OS Ver. 1.1 operating system (the "License"); you may not use this 
file except in compliance with the License.  

Software distributed under the License is distributed on an "AS IS" basis, 
WITHOUT WARRANTY OF ANY KIND, either express or implied.  See the License 
for the specific language governing rights and limitations under the License.
  
The Original Code is VT-OS Ver. 1.1 Operating System, released 
on October 1st, 1999
	
The Initial Developer of the Original Code is VTech Informations Ltd.  All 
codes are Copyright (C) VTech Informations Ltd. 1999.  All Rights Reserved.
===========================================================================
*/

/*
===========================================================================
File        :   dm_user.c
Author(s)   :   Kenny Ng
Company     :   VTech Informations Ltd.
Project     :   Helio 
Date:	    :   October 1st, 1999
Purpose:	:   dynamic memory allocation for user application
Revision    :   1.1
Note        :   None
===========================================================================
*/              


#include "stdafx.h"
#include "datatype.h"
#include "mmu.h"
#include "dm_user.h"
#include "kernel.h"

#ifdef DEBUG
int countqa, countqf;
#endif

#ifdef PR31700
extern UWORD *user_dynamic_mem_address;        /* start address of dynamic memory pool */
extern WORD user_dynamic_mem_size;
extern MatInfoList  mat_info[TOTAL_SLOT];
extern UBYTE dmuser_slot;
extern UBYTE dmos_slot;

typedef struct USER_POOL
{
	WORD	   pool_size;         /* in byte */
	WORD	   owner_id;            /* owner id */
	struct USER_POOL  *size_ptr;
} USER_MEMPOOL;

BYTE user_mem_init;
USER_MEMPOOL *user_free_list;         /* free mem pool link */
static void meminsert_user(USER_MEMPOOL *);
static void memremove_user(USER_MEMPOOL *);

/* meminsert_user                                                    */
/* Purpose : Insert released mem link into free list, sort the list  */
/*           by size in ascending order                              */
/* Input   : ptr        free memory pointer                          */
/* Output  : None                                                    */
/* Return  : None                                                    */
/* Comment : None                                                    */
static void meminsert_user(USER_MEMPOOL *ptr)
{
    USER_MEMPOOL *current = (USER_MEMPOOL *) user_free_list;
	
#ifdef DM_INSERTION_SORT
    USER_MEMPOOL *last    = NULL;
#endif
	
	
    /* check if list is empty */
    if (current == NULL)
    {
		user_free_list = ptr;
		ptr->size_ptr = NULL;
		return;
    }
	
#ifdef DM_INSERTION_SORT
    /* find insertion pointer
	sort by size in ascending order */
    while ((current != NULL) && (current->pool_size > ptr->pool_size))
    {
		last	= current;
		current = current->size_ptr;
    }
	
    /* insert the link */
    if (current == NULL)   /* insert at tail */
    {
		last->size_ptr = ptr;
		ptr->size_ptr  = NULL;
    }
	
    else if (last == NULL)       /* insert at head */
    {
#endif
		
		ptr->size_ptr  = user_free_list;
		user_free_list       = ptr;
		
#ifdef DM_INSERTION_SORT
    }
    else                         /* insert in somewhere else */
    {
		ptr->size_ptr  = current;
		last->size_ptr = ptr;
    }
#endif
}


/* memremove_user                                                    */
/* Purpose : remove a link in free list                              */
/* Input   : ptr         memory pointer                              */
/* Output  : None                                                    */
/* Return  : None                                                    */
/* Comment : None                                                    */
static void memremove_user(USER_MEMPOOL *ptr)
{
    USER_MEMPOOL *current = user_free_list;
    USER_MEMPOOL *last    = NULL;
	
    /* find the link to remove */
    while ((current != NULL) && (current != ptr))
    {
		last	= current;
		current = current->size_ptr;
    }
	
    /* remove the link */
    if (current == NULL)        /* not found */
		user_free_list = NULL;        /* should not occur, if so, empty free list */
	
    else if (last == NULL)     /* the link is in the head */
		user_free_list = ptr->size_ptr;
	
    else                       /* link not in head */
		last->size_ptr = ptr->size_ptr;
}


/* userdminit                                                        */
/* Purpose : reset the user dynamic memory system                    */
/* Input   : none                                                    */
/* Output  : None                                                    */
/* Return  : None                                                    */
/* Comment : None                                                    */
void userdminit(AppID app)
{
    /* set the entire dynamic memory to free list */
    user_dynamic_mem_size = 0;
#ifdef DEBUG
    printf("\nUser Init DM");
#endif
    MemoryDMANew(&mat_info[dmuser_slot],DM_USER, app, 1);
#ifdef DEBUG
    printf("\nInit OK");
#endif
    user_free_list = (USER_MEMPOOL *)user_dynamic_mem_address;   /* start address of dynamic memory */
#ifdef DEBUG
    printf("\ntry to access %08x ", user_free_list);
#endif
    user_free_list->owner_id = app;
#ifdef DEBUG
    printf("\nOK");
#endif
    user_free_list->pool_size = -(user_dynamic_mem_size - 8); /* sizeof(size+owner) = 8 */
    user_free_list->size_ptr    = NULL;
}


/* qmalloc                                                           */
/* Purpose : allocate memory                                         */
/* Input   : size       byte to allocate                             */
/* Output  : None                                                    */
/* Return  : NULL       if fail                                      */
/*           a pointer to allocated memory if success                */
/* Comment : None                                                    */
void *qmalloc(size_t size)
{
    USER_MEMPOOL *current;
    WORD     newsize = size;
    WORD     oldsize, i;
    USER_MEMPOOL *next;
    AppID app_id;
	
    WORD last_size = 0;
    app_id = SysGetActiveAppID();	
    if (!user_mem_init) { userdminit(app_id); user_mem_init = 1; }
	
    current = user_free_list;
	
    newsize += 30;
	
    if (newsize == 0) newsize = 1;
	
    if ( (newsize - newsize / 4 * 4) != 0)
		newsize += 4 - (newsize - newsize / 4 * 4) ;
	
    /* find first free block can hold the size */
	
    newsize = -newsize;              /* free block size is -ve */
    while ((current != NULL) && (current->pool_size > newsize))
    {
		last_size = current->pool_size;
		current = current->size_ptr;
    }
	
    if (current == NULL)   /* allocate  new blocks for dynamic mem */
    {
        if(MemoryTotalFreeBlock(&mat_info[dmuser_slot]) < (-newsize/BLOCK_SIZE + 2))
        {
			printf("\nNo DM Mem");
			return NULL;
        }
		
        for(i=0;i< (-newsize)/BLOCK_SIZE + 1; i++)
			//        if (MemoryDMANew(&mat_info[dmuser_slot], DM_USER, app_id, (USHORT)((-newsize+last_size) / BLOCK_SIZE) + 1) != TRUE)
			if (MemoryDMANew(&mat_info[dmuser_slot], DM_USER, app_id, 1) != TRUE)
			{
				return NULL;   /* no memory */
			}
			
			current = user_free_list;
			while ((current != NULL) && (current->pool_size > newsize))
			{
				current = current->size_ptr;
			}
			
			if(current == NULL)
			{
				return NULL;
			}
    }
    oldsize = current->pool_size;
    memremove_user(current);  /* remove the allocated link from free list */
	
    /* if link size > requested size, insert the extra size to free list */
    if (newsize - oldsize >= 12)
    {
		next = (USER_MEMPOOL *) ((BYTE *) current + 8 - newsize);
		
		next->pool_size = oldsize - newsize + 8;
		meminsert_user(next);
		current->pool_size = -newsize;
    }
    else current->pool_size = -oldsize;
    current->owner_id = app_id;
#ifdef DEBUG
    countqa++;
#endif
	
    return &current->size_ptr;
}


/* qcalloc                                                           */
/* Purpose : allocate memory & set value to NULL                     */
/* Input   : size       byte to allocate                             */
/* Output  : None                                                    */
/* Return  : NULL       if fail                                      */
/*           a pointer to allocated memory if success                */
/* Comment : None                                                    */
void *qcalloc(size_t num, size_t size)
{
    size_t i       = size * num;
    void  *result  = qmalloc(i);
	
    if (result)
    {
		BYTE *cp   = result;
		if (i) do *cp++ = 0; while (--i);
    }
    return result;
}


/* qfree                                                             */
/* Purpose : de-allocate memory                                      */
/* Input   : pool       free memory link                             */
/* Output  : None                                                    */
/* Return  : None                                                    */
/* Comment : None                                                    */
void pfree(void *pool);
void qfree(void *pool)
{
    int tempsize;
    BYTE   *ptr     = (BYTE *) pool - 8;
    USER_MEMPOOL *current = (USER_MEMPOOL *)user_dynamic_mem_address;
    USER_MEMPOOL *next    = NULL;
    USER_MEMPOOL *last    = NULL;
	
    if(pool >= (void*)0x40000000)
    {
        if(pool != NULL)
        {
			pfree(pool);
        }
        return;
    }
	
    /* find the link in dynamic memory */
    if (pool == NULL) return;
#ifdef DEBUG
    countqf++;
#endif
	
    while (current < (USER_MEMPOOL *)ptr)
    {
		tempsize = current->pool_size;
		if (tempsize < 0) tempsize = -tempsize;
		if(tempsize > 8192000)
			return;
		
		last = current;
		current = (USER_MEMPOOL *)((BYTE *) current + tempsize + 8 );
    }
	
	
    /* check if the link is valid */
    if ((current != (USER_MEMPOOL *) ptr) || (current->pool_size < 0))
		return;
	
    current->pool_size = -current->pool_size; /* MARK USER_MEMPOOL AS FREE */
	
    /* find next link */
    next = (USER_MEMPOOL *) ((char *)current - current->pool_size + 8);
	/*    if (next > (USER_MEMPOOL *) &user_dynamic_mem_address[user_dynamic_mem_size - 8]) next = NULL; */
    if ((WORD)(WORD*)next >= ((WORD)(WORD*)user_dynamic_mem_address + user_dynamic_mem_size))
		next = NULL;  /* end of dynamic memory */
	
    /* check if can combine previous, current & next link */
    if ((last != NULL) && (last->pool_size < 0))
    {
		if ((next != NULL) && (next->pool_size < 0))
		{
			memremove_user(last);
			memremove_user(next);
			last->pool_size += next->pool_size + current->pool_size - 16;
			meminsert_user(last);
			return;
		}
		else
		{
			memremove_user(last);
			last->pool_size += current->pool_size - 8;
			meminsert_user(last);
			return;
		}
    }
	
    /* check if can combine current & next link */
    else if ((next != NULL) && (next->pool_size < 0))
    {
		memremove_user(next);
		current->pool_size += next->pool_size - 8;
		meminsert_user(current);
		return;
    }
	
    /* cannot combine with any link */
    else
    {
		meminsert_user(current);
    }
    return;
}

/* rmvgarbage                                                        */
/* Purpose : remove allocated memory for an application              */
/* Input   : pool       free memory link                             */
/* Output  : app_id     ID of the application                        */
/* Return  : None                                                    */
/* Comment :                                                         */
void rmvgarbage(AppID app_id)
{
    USER_MEMPOOL *current = (USER_MEMPOOL *)user_dynamic_mem_address;
    USER_MEMPOOL *next    = NULL;
    USER_MEMPOOL *last    = NULL;
    WORD tempsize;
	
    /* find the link in dynamic memory */
	
    while ((WORD)(WORD*)current < ((WORD)(WORD*)user_dynamic_mem_address + user_dynamic_mem_size))
    {
		tempsize = current->pool_size;
		last = current;
		if (tempsize > 0)  /* allocated link */
		{
			if (current->owner_id == app_id)  /* garbage link */
			{
				/* check if next is free */
				next = (USER_MEMPOOL *)((BYTE *)current + tempsize + 8 );
				if ((WORD)(WORD*)next < ((WORD)(WORD*)user_dynamic_mem_address + user_dynamic_mem_size))
				{
					if (next->pool_size < 0)         /* next is a free link  */
						/* if next link is a free link, free current link      */
						/* will combine with next link, so next link           */
						/* will become invalid, should skip to check next link */
						tempsize += - next->pool_size + 8;  /* skip next link       */
				}
				qfree((WORD*)current+8);
			}
		}
		/* get next */
		else tempsize = -tempsize;
		current = (USER_MEMPOOL *)((WORD *)current + tempsize + 8 );
    }
    return;
}
#endif
