/****************************************************************************/
/*	Copyright (c) 2009, Bendor Research Pty. Ltd. All rights reserved.		*/
/*	This software is released under the GNU General Public License, version	*/
/*	3, with the exemptions that 1) the header files installed by this 	 	*/
/*	package can be included in closed-source code and 2) the libraries		*/
/*	built by this package can be statically linked into a closed-source 	*/
/*	binary. See the file LICENSE for the details.							*/
/*	This software is provided "AS IS", without any warranty or guarantee.	*/
/*	By using this software you acknowledge that neither Bendor Research nor	*/
/*	any of its personnel or affiliates can be held liable for any damages,	*/
/*	including, but not limited to, loss of business, loss of money, loss of */
/*	life, loss or reputation, arising out of this software being faulty or	*/
/*	not performing as advertised, even if Bendor Research was aware of such	*/
/*	faults in the software. You use this software at your own risk, any and */
/*	all liabilities that arise out of your using this software are strictly	*/
/*	yours.																	*/
/****************************************************************************/

#include <nanu-nanu.h>

/****************************************************************************/
/*						Local function prototypes							*/
/****************************************************************************/

static int	NanuBuffAlloc( BC_NANU nanu, unsigned size );
static int	NanuBuffResize( BC_NANU nanu, int buff, unsigned size );

/****************************************************************************/
/*						Local definitions									*/
/****************************************************************************/

/*
*	This structure has 1 information block. Note that
*	members of the block do not really belong to each other.
*	However, each kind of information has the same number of
*	information blocks, so for allocation reasons it's reasonable
*	to bundle them together. All together they fit into 2 ints.
*
*	Note that this structure is not used anywhere else in the library.
*	It is a representation of the actual implementation of the underlying
*	memory management. As long as you implement the _bc_NanuVarXxx() and
*	_bc_NanuStkXxx() functions, the rest of the library doesn't care how
*	you allocate the memory.
*
*	This implementation keeps the memory compacted, that is, all free space
*	is at the end of the pool. If a variable is deleted, then the memory
*	is immediately moved so that there are no holes in it. It means that
*	there's some unnecessary copying which could be avoided, but for now
*	the ultra-simple always compact method is used. Currently the library
*	is used for only the RSA calculations, where the time is spent in
*	exponentiating huge numbers and the memory copy time is negligible
*	compared to the actual calculation time.
*
*	Instead of having three arrays:
*
*	VARIABLE	*stack[ N ];		// These contain references to variables
*	VARIABLE	variables[ N ];		// These contan references to memory blocks
*	BLOCKDESC	memblocks[ N ];		// These are the memory blcok descriptors
*
*	we realise that all three arrays have the same size and thus we herd all
*	the individual structure members into a single structure and form a single
*	array of that. That allows us to compact the the fields, so that we only
*	need 8 bytes per super-structure. The decomposition is as follows:
*
*	NANU[ sp ].vref
*
*		It represents the stack, the index is the stack pointer.
*		The value stored there is an ID to a variable that is at
*		that stack location. The same variable can be referenced
*		from multiple stack locations.
*
*	NANU{ var ].xtrn
*	NANU[ var ].refc
*	NANU[ var ].blck
*
*		These represent a variable with ID 'var'. The xtrn field is a flag
*		which is set to 1 if the variable is an external constant. The
*		refc field is the reference count, how many times this variable
*		is referenced by the stack. The blck field is the ID of the block
*		that actually holds the variable. Each variable refers to a distinct
*		block. Although there's a one-to-one mapping between variables and
*		blocks, the mapping changes as the memory is moved around, the extra
*		indirection is necessary.
*		If the variable is an external constant, it still has a memory block
*		associated with it. The block is large enough to hold a single pointer
*		That is, 1 word on a 32-bit processor) which points to the external
*		data (which, in turn, contains the length and the actual data).
*
*	NANU[ blk ].offs
*	NANU[ blk ].size
*
*		They describe a memory block containing a variable. The index is the
*		block ID. The fields are the size of the variable (in 32-bit words)
*		and its address, in the form of a word offset into the memory pool.
*
*/

typedef struct {

	// This bit describes an allocated block in the pool

	unsigned short	offs;			// Start offset into the pool, in words
	unsigned short	size;			// Length of the number in words

	// This bit describes a variable

	unsigned char	blck;			// A variable's block number
	unsigned char	refc;			// A variable's reference count
	unsigned char	xtrn;			// It is an external variable

	// This bit is the stack

	unsigned char	vref;			// The variable at this stack location

} NANU;

/*
*	This structure holds the state information
*/

struct _bc_s_nanu {

	unsigned int	*base;			// Start pointer into the pool
	unsigned short	size;			// Size of the pool
	unsigned short	last;			// Last used offset
	unsigned char	item;			// Number of items
	unsigned char	used;			// Number of used items
	unsigned char	sptr;			// Stack pointer
	NANU			nanu[];			// Items
};

#define	NANU_UNUSED	255

/****************************************************************************/
/*						User API											*/
/****************************************************************************/

/** @addtogroup nanu */
/** @{ */

/**
*	Initialises the natural number library.
*
*	@param	buff	Pointer to a (word aligned) memory area that will be
*					used as a workspace for the library.
*
*	@param	size	The size of the memory pool <i>in words</i>. The pool
*					should be large enough to store all the numbers that
*					you want simultaneously be in use, plus enough space
*					to hold the library's own administration data. The macro
*					BC_NANU_POOL() can be used to calculate the space needed
*					for the administration.<br>
*					The library has a limited memory handling capability,
*					it can deal with at most 65535 words (i.e. 256KB).<br>
*					Naturally, you are not to touch the memory pool while
*					the natual numbers library is using it.
*
*	@param	item	How deep you want your stack to be. The minimum is 2,
*					the maximum is 127.
*
*	@return			A BC_NANU object, which is an opaque pointer to the
*					natural number state. You will
*					have to pass this object to every natural number
*					function you call. If you specified a memory area
*					that is not large enough to even hold the private
*					administrative data of the library or if you specified
*					a stack depth outside the allowed bounds, then NULL is
*					returned.
*/

BC_NANU	BC_NanuInit( int *buff, unsigned short size, unsigned char item )
{
BC_NANU		nanu;
unsigned	head;

	if ( item < 2 || item > 127 ) return( NULL );
	item++;
	head  = ( sizeof( struct _bc_s_nanu ) + sizeof( int ) - 1 ) / sizeof( int );
	head += ( sizeof( NANU ) * item + sizeof( int ) - 1 ) / sizeof( int );
	if ( size < head + item ) return( NULL );

	nanu	   = (BC_NANU) buff;
	nanu->base = (unsigned *) buff + head;
	nanu->size = size - head;
	nanu->last = 0;
	nanu->item = item;
	nanu->used = 0;
	nanu->sptr = 0;

	for ( head = 0 ; head < item ; head++ )

		nanu->nanu[ head ].blck = NANU_UNUSED;

	return( nanu );
}

/** @} */

/****************************************************************************/
/*				Internally visible functions								*/
/****************************************************************************/

/*
*	Pushes a variable to the stack
*	~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/

int		_bc_NanuStkPut( BC_NANU nanu, int var )
{
	if ( nanu->sptr == nanu->item ) return( BC_NANU_SOVR );
	nanu->nanu[ nanu->sptr++ ].vref = var;
	return( BC_NANU_NANU );
}

/*
*	Pops a variable from the stack
*	~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/

int		_bc_NanuStkGet( BC_NANU nanu )
{
	if ( ! nanu->sptr ) return( BC_NANU_SUND );
	return( nanu->nanu[ --nanu->sptr ].vref );
}

/*
*	Returns the variable at the 'pos' depth of the stack
*	~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*	The stack is unchanged. The reference count of the
*	variable is *NOT* incremented!!!
*/

int		_bc_NanuStkSee( BC_NANU nanu, unsigned pos )
{
	if ( nanu->sptr <= pos ) return( BC_NANU_SUND );
	return( nanu->nanu[ nanu->sptr - 1 - pos ].vref );
}

/*
*	Rolls the stack
*	~~~~~~~~~~~~~~~
*	It implements what BC_NanuRoll() does, see the description there.
*/

int		_bc_NanuStkRol( BC_NANU nanu, int n )
{
int		t, i;
NANU	*s;

	s = nanu->nanu + nanu->sptr - 1;

	if ( n > 0 ) {

		if ( (int) nanu->sptr <= n ) return( BC_NANU_SUND );

		t = s[ -n ].vref;

		for ( i = -n ; i < 0 ; i++ )

			s[ i ].vref = s[ i + 1 ].vref;

		s[ 0 ].vref = t;
	}

	if ( n < 0 ) {

		if ( (int) nanu->sptr <= -n ) return( BC_NANU_SUND );

		t = s[ 0 ].vref;

		for ( i = 0 ; i > n ; i-- )

			s[ i ].vref = s[ i - 1 ].vref;

		s[ n ].vref = t;
	}

	return( BC_NANU_NANU );
}

/*
*	Allocates a new variable
*	~~~~~~~~~~~~~~~~~~~~~~~~
*/

int		_bc_NanuVarNew( BC_NANU nanu, int size )
{
int		var, blk;
NANU	*ptr;

	if ( nanu->used == nanu->item ) {

		// We ran out of slots

		return( BC_NANU_ITEM );
	}

	if ( ( blk = NanuBuffAlloc( nanu, size ) ) < 0 ) {

		// Could not allocate the block

		return( blk );
	}

	// Now find an empty translation slot. It always succeeds.

	for ( var = 0, ptr = nanu->nanu ; ptr->blck != NANU_UNUSED ; ptr++, var++ );

	// Fill in the details and return

	ptr->blck = blk;
	ptr->refc = 1;
	ptr->xtrn = 0;
	return( var );
}

/*
*	Allocates a variable for an external constant
*	~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/

int		_bc_NanuVarCon( BC_NANU nanu, const BC_NANU_CONS *data )
{
int		var;

	if ( ! data->size ) return( BC_NANU_SIZE );

	// For external constants we store the address in a buffer
	// So, we need a buffer large enough to store a pointer.

	var = _bc_NanuVarNew( nanu, sizeof( void * ) / sizeof( int ) );

	if ( var >= 0 ) {

		* ( void **) _bc_NanuVarPtr( nanu, var ) = (void *) data;
		nanu->nanu[ var ].xtrn = 1;
	}

	return( var );
}

/*
*	Returns a pointer to the data of the variable
*	~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/

unsigned	*_bc_NanuVarPtr( BC_NANU nanu, int var )
{
NANU		*vpt, *bpt;

	vpt = nanu->nanu + var;
	bpt = nanu->nanu + vpt->blck;

	if ( vpt->xtrn )

		return( 1 + * (unsigned **) ( nanu->base + bpt->offs ) );
	else
		return( nanu->base + bpt->offs );
}

/*
*	Returns the length of the variable, in words
*	~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/

unsigned	_bc_NanuVarLen( BC_NANU nanu, int var )
{
NANU		*vpt, *bpt;

	vpt = nanu->nanu + var;
	bpt = nanu->nanu + vpt->blck;

	if ( vpt->xtrn )

		return( ** (unsigned **) ( nanu->base + bpt->offs ) );
	else
		return( bpt->size );
}

/*
*	Increment the reference count of a variable
*	~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/

void		_bc_NanuVarInc( BC_NANU nanu, int var )
{
	nanu->nanu[ var ].refc++;
}

/*
*	Decrement the reference count of a variable
*	~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/

void		_bc_NanuVarDec( BC_NANU nanu, int var )
{
int		blk;

	if ( ! --nanu->nanu[ var ].refc ) {

		blk = nanu->nanu[ var ].blck;
		nanu->nanu[ var ].blck = NANU_UNUSED;
		NanuBuffResize( nanu, blk, 0 );
	}
}

/*
*	Resizes a variable
*	~~~~~~~~~~~~~~~~~~
*/

int			_bc_NanuVarRes( BC_NANU nanu, int var, int size )
{
	return( NanuBuffResize( nanu, nanu->nanu[ var ].blck, size ) );
}

/*
*	Normalises a variable
*	~~~~~~~~~~~~~~~~~~~~~
*/

void		_bc_NanuVarNrm( BC_NANU nanu, int var )
{
unsigned	*ptr, siz, len;

	ptr = _bc_NanuVarPtr( nanu, var );
	siz = _bc_NanuVarLen( nanu, var );
	for ( len = siz, ptr += siz ; len > 1 && ! *--ptr ; len-- );
	if ( siz != len ) _bc_NanuVarRes( nanu, var, len );
}

/*
*	Creates a temporary
*	~~~~~~~~~~~~~~~~~~~
*	Returns a variable that has the size *at least* the given size. It
*	can receive a reference for up to two other variables. Those are
*	variables that the caller knows will not be needed later, therefore
*	they can be used instead of a newly allocated variable, assuming that
*	they can be resized for the requested length. This routine checks if
*	those variables are indeed real variables (and not external constants)
*	and that they are not referenced elsewhere. If that is the case, then
*	it choses one of them and if the requested size is larger than the
*	variable's current size, it resizes it, without changing its content.
*	If the requested size is smaller, there's no resizing. That behaviour
*	guarantees that the original content of the variable is still intact.
*/

int			_bc_NanuVarTmp( BC_NANU nanu, int var1, int var2, unsigned size )
{
unsigned	len1, len2;

	if ( var2 < 0 || nanu->nanu[ var2 ].xtrn || nanu->nanu[ var2 ].refc > 1 ) {

		var2 = -1;
	}

	if ( var1 < 0 || nanu->nanu[ var1 ].xtrn || nanu->nanu[ var1 ].refc > 1 ) {

		if ( var2 == -1 ) return( _bc_NanuVarNew( nanu, size ) );

		var1 = var2;
		var2 = -1;
	}

	// At least var1 is available. If we have a choice, we chose the one that
	// is larger. If they are equal in size, we chose the one which has the
	// higher buffer ID.

	len1 = _bc_NanuVarLen( nanu, var1 );

	if ( var2 >= 0 ) {

		len2 = _bc_NanuVarLen( nanu, var2 );

		if ( len2 > len1 ||
			 ( len2 == len1 &&
			   nanu->nanu[ var2 ].blck > nanu->nanu[ var1 ].blck ) ) {

			var1 = var2;
			len1 = len2;
		}
	}

	_bc_NanuVarInc( nanu, var1 );

	// We only resize the variable if the requested size is larger than the
	// current size.

	if ( len1 < size && _bc_NanuVarRes( nanu, var1, size ) < 0 )

		return( BC_NANU_OMEM );

	return( var1 );
}

/*
*	In case of an error occured resets the state
*	~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/

int		_bc_NanuError( BC_NANU nanu, int error )
{
unsigned	i;

	if ( error < 0 ) {

		for ( i = 0 ; i < nanu->item ; i++ )

			nanu->nanu[ i ].blck = NANU_UNUSED;

		nanu->sptr = 0;
		nanu->used = 0;
	}

	return( error );
}

/****************************************************************************/
/*						Local functions										*/
/****************************************************************************/

/*
*	Allocates a new buffer
*	~~~~~~~~~~~~~~~~~~~~~~
*/

static int	NanuBuffAlloc( BC_NANU nanu, unsigned size )
{
	if ( nanu->used == nanu->item ) {

		// We ran out of descriptors

		return( BC_NANU_ITEM );
	}

	if ( nanu->size - nanu->last < (int) size ) {

		// We don't have enough memory

		return( BC_NANU_OMEM );
	}

	// Just allocate it and return the descriptor

	nanu->nanu[ nanu->used ].offs = nanu->last;
	nanu->nanu[ nanu->used ].size = size;
	nanu->last += size;
	return( nanu->used++ );
}

/*
*	Resizes an existing buffer
*	~~~~~~~~~~~~~~~~~~~~~~~~~~
*/

static int	NanuBuffResize( BC_NANU nanu, int buff, unsigned size )
{
unsigned	chge, offs;

	if ( size == nanu->nanu[ buff ].size ) {

		// No change, we succeed in doing nothing

		return( BC_NANU_NANU );
	}

	if ( size < nanu->nanu[ buff ].size ) {

		// The buffer shrinks. Set 'offs' to the new end of the
		// buffer and chge to the number of words discarded from it.

		chge = nanu->nanu[ buff ].size - size;
		offs = nanu->nanu[ buff ].offs + size;

		if ( buff != nanu->used - 1 ) {

			// This is not the last item, move things. From the
			// new end + change (i.e. old end) to the new end
			// we copy the size of the entire buffer area
			// minus the old end.

			_bc_NanuMove( nanu->base + offs,
						  nanu->base + offs + chge,
						  nanu->last - offs - chge );

			// From the current buffer up each buffer's start
			// offset should be decreased by the change amount

			for ( offs = buff + 1 ; offs < nanu->used ; offs++ ) {

				nanu->nanu[ offs ].offs -= chge;
			}
		}

		// Adjust the buffer size and the end of the allocated area.

		nanu->nanu[ buff ].size -= chge;
		nanu->last -= chge;

		if ( size == 0 ) {

			// The buffer is actually deleted. We remove its descriptor
			// completely and adjust all future descriptors accordingly.

			if ( buff != nanu->used - 1 ) {

				// First, shift buffer descriptors following the
				// current one so that there's no empty descriptor

				for ( offs = buff ; offs + 1 < nanu->used ; offs++ ) {

					nanu->nanu[ offs ].offs = nanu->nanu[ offs + 1 ].offs;
					nanu->nanu[ offs ].size = nanu->nanu[ offs + 1 ].size;
				}

				// Then go through the variable list and if a variable
				// refers to a buffer that has an index larger than the
				// current one, then decrement the index to cater for
				// the buffer shifting done above

				for ( offs = 0 ; offs < nanu->item ; offs++ ) {

					if ( nanu->nanu[ offs ].blck != NANU_UNUSED &&
						 nanu->nanu[ offs ].blck > buff )

						nanu->nanu[ offs ].blck--;
				}
			}

			// The number of used buffer descriptors should be adjusted

			nanu->used--;
		}
	}
	else {

		// The buffer has to be enlarged. See if we have enough space
		// Set change to the number of words to be allocated and offs
		// to the current end of the buffer

		chge = size - nanu->nanu[ buff ].size;
		offs = nanu->nanu[ buff ].offs + nanu->nanu[ buff ].size;

		if ( nanu->size < nanu->last + chge ) {

			// Not enough memory

			return( BC_NANU_OMEM );
		}

		if ( buff != nanu->used - 1 ) {

			// We have buffers behind us, we have to move the data

			_bc_NanuMove( nanu->base + offs + chge,
						  nanu->base + offs,
						  nanu->last - offs );
		}

		// Adjust the buffer itself and the allocated memory size

		nanu->nanu[ buff ].size = size;
		nanu->last += chge;

		// Adjust all buffer starts above the current

		for ( offs = buff + 1 ; offs < nanu->item ; offs++ ) {

			nanu->nanu[ offs ].offs += chge;
		}
	}

	return( BC_NANU_NANU );
}

