/****************************************************************************
*																			*
*						  Memory Stream I/O Functions						*
*						Copyright Peter Gutmann 1993-2004					*
*																			*
****************************************************************************/

#include <string.h>
#if defined( INC_ALL )
  #include "stream.h"
#elif defined( INC_CHILD )
  #include "stream.h"
#else
  #include "io/stream.h"
#endif /* Compiler-specific includes */

/* Initialise and shut down a memory stream */

static int initMemoryStream( STREAM *stream, const void *buffer, 
							 const int length, const BOOLEAN nullStreamOK )
	{
	/* Check that the input parameters are in order.  Since the return
	   value for the memory stream open functions is rarely (if ever) 
	   checked, we validate the buffer and length parameters later and 
	   create a read-only null stream if they're invalid, so that reads and 
	   writes return error conditions if they're attempted */
	if( !isWritePtr( stream, sizeof( STREAM ) ) )
		{
		assert( NOTREACHED );
		return( CRYPT_ERROR_WRITE );
		}

	/* Clear the stream data and make it a null stream if required */
	memset( stream, 0, sizeof( STREAM ) );
	if( nullStreamOK && buffer == NULL && length == 0 )
		{
		stream->type = STREAM_TYPE_NULL;
		return( CRYPT_OK );
		}

	/* If there's a problem with the parameters, return an error code but 
	   also make it a (non-readable, non-writeable) null stream so that it 
	   can be safely used */
	if( length < 1 || !isReadPtr( buffer, length ) )
		{
		assert( NOTREACHED );
		stream->type = STREAM_TYPE_NULL;
		stream->flags = STREAM_FLAG_READONLY;
		return( CRYPT_ERROR_WRITE );
		}

	/* Initialise the stream structure */
	stream->type = STREAM_TYPE_MEMORY;
	stream->buffer = ( void * ) buffer;
	stream->bufSize = length;

	return( CRYPT_OK );
	}

static int shutdownMemoryStream( STREAM *stream, 
								 const BOOLEAN clearStreamBuffer )
	{
	/* Check that the input parameters are in order */
	if( !isWritePtr( stream, sizeof( STREAM ) ) )
		{
		assert( NOTREACHED );
		return( CRYPT_ERROR_WRITE );
		}

	/* Clear the stream structure */
	if( clearStreamBuffer && stream->buffer != NULL && stream->bufEnd > 0 )
		zeroise( stream->buffer, stream->bufEnd );
	zeroise( stream, sizeof( STREAM ) );

	return( CRYPT_OK );
	}

/* Open/close a memory stream.  If the buffer parameter is NULL and the 
   length is zero, this creates a null stream that serves as a data sink - 
   this is useful for implementing sizeof() functions by writing data to 
   null streams */

int sMemOpen( STREAM *stream, void *buffer, const int length )
	{
	int status;

	assert( isWritePtr( stream, sizeof( STREAM ) ) );
	assert( ( buffer == NULL && length == 0 ) || \
			isWritePtr( buffer, length ) );

	/* Initialise the memory stream */
	status = initMemoryStream( stream, buffer, length, TRUE );
	if( cryptStatusError( status ) )
		return( status );

	/* If it's not a null stream, clear the stream buffer.  Since this can 
	   be arbitrarily large, we only clear the entire buffer in the debug 
	   version */
	if( buffer != NULL )
		{
#ifdef NDEBUG
		memset( stream->buffer, 0, min( 16, stream->bufSize ) );
#else
		assert( isWritePtr( buffer, length ) );
		memset( stream->buffer, 0, stream->bufSize );
#endif /* NDEBUG */
		}

	return( CRYPT_OK );
	}

int sMemClose( STREAM *stream )
	{
	assert( isWritePtr( stream, sizeof( STREAM ) ) );
	assert( stream->type == STREAM_TYPE_NULL || \
			stream->type == STREAM_TYPE_MEMORY );
	assert( !( stream->flags & STREAM_FLAG_READONLY ) );

	return( shutdownMemoryStream( stream, TRUE ) );
	}

/* Connect/disconnect a memory stream without destroying the buffer 
   contents */

int sMemConnect( STREAM *stream, const void *buffer, const int length )
	{
	int status;

	assert( isWritePtr( stream, sizeof( STREAM ) ) );
	assert( length >= 1 );
	assert( isReadPtr( buffer, length ) );

	/* Initialise the memory stream */
	status = initMemoryStream( stream, buffer, length, FALSE );
	if( cryptStatusError( status ) )
		return( status );

	/* Initialise further portions of the stream structure */
	stream->bufEnd = length;
	stream->flags = STREAM_FLAG_READONLY;

	return( CRYPT_OK );
	}

int sMemDisconnect( STREAM *stream )
	{
	assert( isWritePtr( stream, sizeof( STREAM ) ) );
	assert( stream->type == STREAM_TYPE_NULL || \
			stream->type == STREAM_TYPE_MEMORY );

	return( shutdownMemoryStream( stream, FALSE ) );
	}
