/*****************************************************************************/
// Copyright 2006-2007 Adobe Systems Incorporated
// All Rights Reserved.
//
// NOTICE:  Adobe permits you to use, modify, and distribute this file in
// accordance with the terms of the Adobe license agreement accompanying it.
/*****************************************************************************/

/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_memory_stream.cpp#1 $ */ 
/* $DateTime: 2012/05/30 13:28:51 $ */
/* $Change: 832332 $ */
/* $Author: tknoll $ */

/*****************************************************************************/

#include "dng_memory_stream.h"

#include "dng_bottlenecks.h"
#include "dng_exceptions.h"
#include "dng_safe_arithmetic.h"
#include "dng_utils.h"

/*****************************************************************************/

dng_memory_stream::dng_memory_stream (dng_memory_allocator &allocator,
									  dng_abort_sniffer *sniffer,
						   			  uint32 pageSize)
						   			  
	:	dng_stream (sniffer, 
					kDefaultBufferSize,
					kDNGStreamInvalidOffset)
	
	,	fAllocator (allocator)
	,	fPageSize  (pageSize )
	
	,	fPageCount      (0)
	,	fPagesAllocated (0)
	,	fPageList       (NULL)
	
	,	fMemoryStreamLength (0)
	
	{
	
	}
		
/*****************************************************************************/

dng_memory_stream::~dng_memory_stream ()
	{
	
	if (fPageList)
		{
		
		for (uint32 index = 0; index < fPageCount; index++)
			{
			
			delete fPageList [index];
			
			}
			
		free (fPageList);
		
		}
	
	}
		
/*****************************************************************************/
		
uint64 dng_memory_stream::DoGetLength ()
	{
	
	return fMemoryStreamLength;
	
	}
		
/*****************************************************************************/
		
void dng_memory_stream::DoRead (void *data,
							    uint32 count,
							    uint64 offset)
	{
	
	if (offset + count > fMemoryStreamLength)
		{
		
		ThrowEndOfFile ();
		
		}
		
	uint64 baseOffset = offset;
	
	while (count)
		{
		
		uint32 pageIndex  = (uint32) (offset / fPageSize);
		uint32 pageOffset = (uint32) (offset % fPageSize);
		
		uint32 blockCount = Min_uint32 (fPageSize - pageOffset, count);
		
		const uint8 *sPtr = fPageList [pageIndex]->Buffer_uint8 () +
						    pageOffset;
		
		uint8 *dPtr = ((uint8 *) data) + (uint32) (offset - baseOffset);
		
		DoCopyBytes (sPtr, dPtr, blockCount);
		
		offset += blockCount;
		count  -= blockCount;
		
		}
	
	}
							 
/*****************************************************************************/

void dng_memory_stream::DoSetLength (uint64 length)
	{
	
	while (length > fPageCount * (uint64) fPageSize)
		{
		
		if (fPageCount == fPagesAllocated)
			{
			
			uint32 newSizeTemp1 = 0, newSizeTemp2 = 0;
			if (!SafeUint32Add (fPagesAllocated, 32u, &newSizeTemp1) ||
				!SafeUint32Mult (fPagesAllocated, 2u, &newSizeTemp2))
				{
				ThrowMemoryFull ("Arithmetic overflow in DoSetLength()");
				}
			uint32 newSize = Max_uint32 (newSizeTemp1, newSizeTemp2);
			uint32 numBytes;
			if (!SafeUint32Mult (newSize, sizeof (dng_memory_block *),
								 &numBytes))
				{
				ThrowMemoryFull ("Arithmetic overflow in DoSetLength()");
				}
			
			dng_memory_block **list = (dng_memory_block **) malloc (numBytes);
			
			if (!list)
				{
				
				ThrowMemoryFull ();
				
				}
			
			if (fPageCount)
				{
				
				// The multiplication here is safe against overflow. fPageCount
				// can never reach a value that is large enough to cause
				// overflow because the computation of numBytes above would fail
				// before a list of that size could be allocated.
				DoCopyBytes (fPageList,
							 list,
							 fPageCount * (uint32) sizeof (dng_memory_block *));
				
				}
				
			if (fPageList)
				{
				
				free (fPageList);
				
				}
				
			fPageList = list;
			
			fPagesAllocated = newSize;
			
			}
			
		fPageList [fPageCount] = fAllocator.Allocate (fPageSize);
		
		fPageCount++;
		
		}
		
	fMemoryStreamLength = length;
	
	}
							 
/*****************************************************************************/

void dng_memory_stream::DoWrite (const void *data,
							     uint32 count,
							     uint64 offset)
	{
	
	DoSetLength (Max_uint64 (fMemoryStreamLength,
							 offset + count));
	
	uint64 baseOffset = offset;
	
	while (count)
		{
		
		uint32 pageIndex  = (uint32) (offset / fPageSize);
		uint32 pageOffset = (uint32) (offset % fPageSize);
		
		uint32 blockCount = Min_uint32 (fPageSize - pageOffset, count);
		
		const uint8 *sPtr = ((const uint8 *) data) + (uint32) (offset - baseOffset);
		
		uint8 *dPtr = fPageList [pageIndex]->Buffer_uint8 () +
					  pageOffset;
		
		DoCopyBytes (sPtr, dPtr, blockCount);
		
		offset += blockCount;
		count  -= blockCount;
		
		}
	
	}
		
/*****************************************************************************/

void dng_memory_stream::CopyToStream (dng_stream &dstStream,
									  uint64 count)
	{
	
	if (count < kBigBufferSize)
		{
	
		dng_stream::CopyToStream (dstStream, count);
		
		}
		
	else
		{
		
		Flush ();
		
		uint64 offset = Position ();
		
		if (offset + count > Length ())
			{
			
			ThrowEndOfFile ();
			
			}
			
		while (count)
			{
			
			uint32 pageIndex  = (uint32) (offset / fPageSize);
			uint32 pageOffset = (uint32) (offset % fPageSize);
			
			uint32 blockCount = (uint32) Min_uint64 (fPageSize - pageOffset, count);
			
			const uint8 *sPtr = fPageList [pageIndex]->Buffer_uint8 () +
								pageOffset;
								
			dstStream.Put (sPtr, blockCount);
			
			offset += blockCount;
			count  -= blockCount;
			
			}
			
		SetReadPosition (offset);
	
		}
	
	}
		
/*****************************************************************************/