/*
 * Copyright (C) 2008 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/* ---- includes ----------------------------------------------------------- */

#include "b_BasicEm/Context.h"
#include "b_BasicEm/MemTbl.h"
#include "b_BasicEm/Functions.h"

/* ------------------------------------------------------------------------- */

/* ========================================================================= */
/*                                                                           */
/* ---- \ghd{ auxiliary functions } ---------------------------------------- */
/*                                                                           */
/* ========================================================================= */

/* ------------------------------------------------------------------------- */

flag bbs_MemTbl_memOverlap( const uint16* memPtr1A, uint32 size1A, 
						    const uint16* memPtr2A, uint32 size2A )
{
	int32 diffL = memPtr2A - memPtr1A;
	if( diffL >= 0 && diffL < ( int32 )size1A ) return TRUE;
	diffL += ( int32 )size2A;
	if( diffL >= 0 && diffL < ( int32 )size1A ) return TRUE;
	return FALSE;
}

/* ------------------------------------------------------------------------- */

/* ========================================================================= */
/*                                                                           */
/* ---- \ghd{ constructor / destructor } ----------------------------------- */
/*                                                                           */
/* ========================================================================= */

/* ------------------------------------------------------------------------- */

void bbs_MemTbl_init( struct bbs_Context* cpA,
					  struct bbs_MemTbl* ptrA )
{
	uint32 iL;
	for( iL = 0; iL < bbs_MAX_MEM_SEGS; iL++ )
	{
		bbs_MemSeg_init( cpA, &ptrA->esArrE[ iL ] );
		bbs_MemSeg_init( cpA, &ptrA->ssArrE[ iL ] );
		ptrA->espArrE[ iL ] = NULL;
	}
	ptrA->esSizeE = 0;
	ptrA->ssSizeE = 0;
}

/* ------------------------------------------------------------------------- */

void bbs_MemTbl_exit( struct bbs_Context* cpA,
					  struct bbs_MemTbl* ptrA )
{
	uint32 iL;
	for( iL = 0; iL < bbs_MAX_MEM_SEGS; iL++ )
	{
		bbs_MemSeg_exit( cpA, &ptrA->esArrE[ iL ] );
		bbs_MemSeg_exit( cpA, &ptrA->ssArrE[ iL ] );
		ptrA->espArrE[ iL ] = NULL;
	}
	ptrA->esSizeE = 0;
	ptrA->ssSizeE = 0;
}

/* ------------------------------------------------------------------------- */

/* ========================================================================= */
/*                                                                           */
/* ---- \ghd{ operators } -------------------------------------------------- */
/*                                                                           */
/* ========================================================================= */

/* ------------------------------------------------------------------------- */

/* ========================================================================= */
/*                                                                           */
/* ---- \ghd{ query functions } -------------------------------------------- */
/*                                                                           */
/* ========================================================================= */

/* ------------------------------------------------------------------------- */

flag bbs_MemTbl_overlap( struct bbs_Context* cpA,
						 struct bbs_MemTbl* ptrA, 
						 const void* memPtrA, uint32 sizeA )
{
	uint32 iL;
	for( iL = 0; iL < ptrA->esSizeE; iL++ )
	{
		if( bbs_MemTbl_memOverlap( ptrA->espArrE[ iL ]->memPtrE, 
								   ptrA->espArrE[ iL ]->sizeE,
								   memPtrA, sizeA ) )
		{
			return TRUE;
		}
	}

	for( iL = 0; iL < ptrA->ssSizeE; iL++ )
	{
		if( bbs_MemTbl_memOverlap( ptrA->ssArrE[ iL ].memPtrE, 
								   ptrA->ssArrE[ iL ].sizeE,
								   memPtrA, sizeA ) )
		{
			return TRUE;
		}
	}

	return FALSE;
}

/* ------------------------------------------------------------------------- */

/* ========================================================================= */
/*                                                                           */
/* ---- \ghd{ modify functions } ------------------------------------------- */
/*                                                                           */
/* ========================================================================= */

/* ------------------------------------------------------------------------- */
	
/* ========================================================================= */
/*                                                                           */
/* ---- \ghd{ I/O } -------------------------------------------------------- */
/*                                                                           */
/* ========================================================================= */

/* ------------------------------------------------------------------------- */
	
/* ========================================================================= */
/*                                                                           */
/* ---- \ghd{ exec functions } --------------------------------------------- */
/*                                                                           */
/* ========================================================================= */
	
/* ------------------------------------------------------------------------- */

void bbs_MemTbl_create( struct bbs_Context* cpA,
					    struct bbs_MemTbl* ptrA, 
						void* memPtrA, 
						uint32 sizeA, 
						uint32 sharedSubSizeA )
{
	if( sharedSubSizeA > sizeA )
	{
		bbs_ERROR0( "struct bbs_MemTbl bbs_MemTbl_create( void* memPtrA, uint32 sizeA, uint32 sharedSubSizeA ):\n"
			       "sharedSubSizeA > sizeA" );
		return;
	}
	bbs_MemTbl_init( cpA, ptrA );

	
	ptrA->esArrE[ 0 ] = bbs_MemSeg_create( cpA, memPtrA, sizeA - sharedSubSizeA );
	#ifdef HW_TMS320C5x		
		ptrA->ssArrE[ 0 ] = bbs_MemSeg_createShared( cpA, ( uint16* ) ( ( int32 ) ( ( uint16* )memPtrA ) + sizeA - sharedSubSizeA ), sharedSubSizeA );
	#else
		ptrA->ssArrE[ 0 ] = bbs_MemSeg_createShared( cpA, ( uint16* )memPtrA + sizeA - sharedSubSizeA, sharedSubSizeA );
	#endif
	ptrA->espArrE[ 0 ] = &ptrA->esArrE[ 0 ];

	ptrA->esSizeE = 1;
	ptrA->ssSizeE = 1;
}

/* ------------------------------------------------------------------------- */

void bbs_MemTbl_add( struct bbs_Context* cpA,
					 struct bbs_MemTbl* ptrA, 
					 void* memPtrA, 
					 uint32 sizeA, 
					 uint32 idA )
{
	if( ptrA->esSizeE == bbs_MAX_MEM_SEGS )
	{
		bbs_ERROR0( "void bbs_MemTbl_add( struct bbs_MemTbl* ptrA, void* memPtrA, uint32 sizeA ):\n"
			       "Table is full! Increase constant bbs_MAX_MEM_SEGS" );
		return;
	}
	ptrA->esArrE[ ptrA->esSizeE ] = bbs_MemSeg_create( cpA, memPtrA, sizeA );
	ptrA->esArrE[ ptrA->esSizeE ].idE = idA;
	ptrA->espArrE[ ptrA->esSizeE ] = &ptrA->esArrE[ ptrA->esSizeE ];
	ptrA->esSizeE++;
}

/* ------------------------------------------------------------------------- */

void bbs_MemTbl_addShared( struct bbs_Context* cpA,
						   struct bbs_MemTbl* ptrA, 
						   void* memPtrA, 
						   uint32 sizeA, 
						   uint32 idA )
{
	if( ptrA->ssSizeE == bbs_MAX_MEM_SEGS )
	{
		bbs_ERROR0( "void bbs_MemTbl_addShared( struct bbs_MemTbl* ptrA, void* memPtrA, uint32 sizeA ):\n"
			       "Table is full! Increase constant bbs_MAX_MEM_SEGS" );
		return;
	}
	ptrA->ssArrE[ ptrA->ssSizeE ] = bbs_MemSeg_createShared( cpA, memPtrA, sizeA );
	ptrA->ssArrE[ ptrA->ssSizeE ].idE = idA;
	ptrA->ssSizeE++;
}

/* ------------------------------------------------------------------------- */

struct bbs_MemSeg* bbs_MemTbl_segPtr( struct bbs_Context* cpA,
									  struct bbs_MemTbl* ptrA, 
									  uint32 idA )
{
	uint32 iL;
	if( ptrA->esSizeE == 0 )
	{
		bbs_ERROR0( "bbs_MemTbl_segPtr(): Table contains no exclusive segments." );
		return NULL;
	}
	if( idA > 0 ) 
	{
		for( iL = 0; iL < ptrA->esSizeE; iL++ )
		{
			if( idA == ptrA->espArrE[ iL ]->idE ) return ptrA->espArrE[ iL ];
		}
	}
	for( iL = 0; iL < ptrA->esSizeE; iL++ )
	{
		if( ptrA->espArrE[ iL ]->sizeE > 0 ||
			ptrA->espArrE[ iL ]->dynMemManagerPtrE != 0 )
		{
			return ptrA->espArrE[ iL ];
		}
	}
	bbs_ERR0( bbs_ERR_MEMORY_OVERFLOW,
			  "bbs_MemTbl_segPtr(): Table contains no valid exclusive segments." );
	return 0;
}

/* ------------------------------------------------------------------------- */

struct bbs_MemSeg* bbs_MemTbl_sharedSegPtr( struct bbs_Context* cpA,
										    struct bbs_MemTbl* ptrA, 
											uint32 idA )
{
	uint32 iL;
	if( ptrA->ssSizeE == 0 )
	{
		bbs_ERROR0( "bbs_MemTbl_sharedSegPtr(): Table contains no shared segments." );
		return NULL;
	}
	if( idA > 0 ) 
	{
		for( iL = 0; iL < ptrA->ssSizeE; iL++ )
		{
			if( idA == ptrA->ssArrE[ iL ].idE ) return &ptrA->ssArrE[ iL ];
		}
	}
	for( iL = 0; iL < ptrA->ssSizeE; iL++ )
	{
		if( ptrA->ssArrE[ iL ].sizeE > 0 ||
			ptrA->ssArrE[ iL ].dynMemManagerPtrE != 0 )
		{
			return &ptrA->ssArrE[ iL ];
		}
	}
	bbs_ERR0( bbs_ERR_MEMORY_OVERFLOW,
			  "bbs_MemTbl_sharedSegPtr(): Table contains no valid shared segments." );
	return 0;
}

/* ------------------------------------------------------------------------- */

struct bbs_MemSeg* bbs_MemTbl_fastestSegPtr( struct bbs_Context* cpA,
											 struct bbs_MemTbl* ptrA, 
											 uint32 minSizeA )
{
	uint32 iL;
	for( iL = 0; iL < ptrA->esSizeE; iL++ )
	{
		if( bbs_MemSeg_availableSize( cpA, ptrA->espArrE[ iL ] ) >= minSizeA ) break;
	}
	if( iL == ptrA->esSizeE )
	{
		if( ptrA->esSizeE == 0 )
		{
			bbs_ERROR0( "struct bbs_MemSeg* bbs_MemTbl_fastestSegPtr( struct bbs_MemTbl* ptrA, uint32 minSizeA ):\n"
					   "Table contains no exclusive segments" );
			return NULL;
		}
		else
		{
			bbs_ERR0( bbs_ERR_MEMORY_OVERFLOW,
					  "struct bbs_MemSeg* bbs_MemTbl_fastestSegPtr( struct bbs_MemTbl* ptrA, uint32 minSizeA ):\n"
					  "Could not find segment with sufficient free space" );
			return NULL;
		}
	}
	if( ptrA->espArrE[ iL ]->sharedE )
	{
		bbs_ERROR0( "struct bbs_MemSeg* bbs_MemTbl_fastestSegPtr( struct bbs_MemTbl* ptrA, uint32 minSizeA ):\n"
			       "Table corrupt: Found shared segment in exclusive table" );
		return NULL;
	}

	return ptrA->espArrE[ iL ];
}

/* ------------------------------------------------------------------------- */

struct bbs_MemSeg* bbs_MemTbl_largestSegPtr( struct bbs_Context* cpA,
											 struct bbs_MemTbl* ptrA )
{
	uint32 iL;
	uint32 maxIndexL = 0;
	uint32 maxSizeL = 0;

	if( ptrA->esSizeE == 0 )
	{
		bbs_ERROR0( "struct bbs_MemSeg* bbs_MemTbl_largestSegPtr( struct bbs_MemTbl* ptrA ):\n"
			       "No exclusive segments available" );
		return NULL;
	}

	for( iL = 0; iL < ptrA->esSizeE; iL++ )
	{
		uint32 sizeL = bbs_MemSeg_availableSize( cpA, ptrA->espArrE[ iL ] );
		if( sizeL > maxSizeL )
		{
			maxSizeL = sizeL;
			maxIndexL = iL;
		}
	}

	if( ptrA->espArrE[ maxIndexL ]->sharedE )
	{
		bbs_ERROR0( "struct bbs_MemSeg* bbs_MemTbl_largestSegPtr( struct bbs_MemTbl* ptrA ):\n"
			       "Table corrupt: Found shared segment in exclusive table" );
		return NULL;
	}

	return ptrA->espArrE[ maxIndexL ];
}

/* ------------------------------------------------------------------------- */

struct bbs_MemSeg* bbs_MemTbl_fastestSharedSegPtr( struct bbs_Context* cpA,
												   struct bbs_MemTbl* ptrA, 
												   uint32 minSizeA )
{
	uint32 iL;
	for( iL = 0; iL < ptrA->ssSizeE; iL++ )
	{
		if( bbs_MemSeg_availableSize( cpA, &ptrA->ssArrE[ iL ] ) >= minSizeA ) break;
	}
	if( iL == ptrA->ssSizeE )
	{
		if( ptrA->esSizeE == 0 )
		{
			bbs_ERROR0( "struct bbs_MemSeg* bbs_MemTbl_fastestSegPtr( struct bbs_MemTbl* ptrA, uint32 minSizeA ):\n"
					   "Table contains no shared segments" );
			return NULL;
		}
		else
		{
			bbs_ERR0( bbs_ERR_MEMORY_OVERFLOW, 
					  "struct bbs_MemSeg* bbs_MemTbl_fastestSharedSegPtr( struct bbs_MemTbl* ptrA, uint32 minSizeA ):\n"
					  "Could not find segment with sufficient free space" );
			return NULL;
		}
	}
	if( !ptrA->ssArrE[ iL ].sharedE )
	{
		bbs_ERROR0( "struct bbs_MemSeg* bbs_MemTbl_fastestSharedSegPtr( struct bbs_MemTbl* ptrA, uint32 minSizeA ):\n"
			       "Table corrupt: Found exclusive segment in shared table" );
		return NULL;
	}

	return &ptrA->ssArrE[ iL ];
}

/* ------------------------------------------------------------------------- */

struct bbs_MemSeg* bbs_MemTbl_largestSharedSegPtr( struct bbs_Context* cpA,
												   struct bbs_MemTbl* ptrA )
{
	uint32 iL;
	uint32 maxIndexL = 0;
	uint32 maxSizeL = 0;

	if( ptrA->ssSizeE == 0 )
	{
		bbs_ERROR0( "struct bbs_MemSeg* bbs_MemTbl_largestSharedSegPtr( struct bbs_MemTbl* ptrA ):\n"
			       "No shared segments available" );
		return NULL;
	}

	for( iL = 0; iL < ptrA->ssSizeE; iL++ )
	{
		uint32 sizeL = bbs_MemSeg_availableSize( cpA, &ptrA->ssArrE[ iL ] );
		if( sizeL > maxSizeL )
		{
			maxSizeL = sizeL;
			maxIndexL = iL;
		}
	}

	if( !ptrA->ssArrE[ maxIndexL ].sharedE )
	{
		bbs_ERROR0( "struct bbs_MemSeg* bbs_MemTbl_largestSharedSegPtr( struct bbs_MemTbl* ptrA ):\n"
			       "Table corrupt: Found exclusive segment in shared table" );
		return NULL;
	}

	return &ptrA->ssArrE[ maxIndexL ];
}

/* ------------------------------------------------------------------------- */

/* ========================================================================= */