/*****************************************************************************/
// 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_stream.cpp#2 $ */ 
/* $DateTime: 2012/06/01 07:28:57 $ */
/* $Change: 832715 $ */
/* $Author: tknoll $ */

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

#include "dng_stream.h"

#include "dng_abort_sniffer.h"
#include "dng_auto_ptr.h"
#include "dng_bottlenecks.h"
#include "dng_exceptions.h"
#include "dng_flags.h"
#include "dng_memory.h"
#include "dng_tag_types.h"

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

dng_stream::dng_stream (dng_abort_sniffer *sniffer,
						uint32 bufferSize,
						uint64 offsetInOriginalFile)

	:	fSwapBytes			  (false)
	,	fHaveLength			  (false)
	,	fLength				  (0)
	,	fOffsetInOriginalFile (offsetInOriginalFile)
	,	fPosition			  (0)
	,	fMemBlock			  (bufferSize)
	,	fBuffer				  (fMemBlock.Buffer_uint8 ())
	,	fBufferSize			  (bufferSize)
	,	fBufferStart		  (0)
	,	fBufferEnd			  (0)
	,	fBufferLimit		  (bufferSize)
	,	fBufferDirty		  (false)
	,	fSniffer			  (sniffer)
	
	{
	
	}
		
/*****************************************************************************/

dng_stream::dng_stream (const void *data,
						uint32 count,
						uint64 offsetInOriginalFile)
						
	:	fSwapBytes			  (false)
	,	fHaveLength			  (true)
	,	fLength				  (count)
	,	fOffsetInOriginalFile (offsetInOriginalFile)
	,	fPosition			  (0)
	,	fMemBlock			  ()
	,	fBuffer				  ((uint8 *) data)
	,	fBufferSize			  (count)
	,	fBufferStart		  (0)
	,	fBufferEnd			  (count)
	,	fBufferLimit		  (count)
	,	fBufferDirty		  (false)
	,	fSniffer			  (NULL)
	
	{
	
	}
		
/*****************************************************************************/

dng_stream::~dng_stream ()
	{
	
	}
		
/*****************************************************************************/

uint64 dng_stream::DoGetLength ()
	{
	
	ThrowProgramError ();

	return 0;
	
	}
		
/*****************************************************************************/

void dng_stream::DoRead (void * /* data */,
						 uint32 /* count */,
						 uint64 /* offset */)
	{
	
	ThrowProgramError ();

	}
		
/*****************************************************************************/

void dng_stream::DoSetLength (uint64 /* length */)
	{
	
	ThrowProgramError ();

	}
		
/*****************************************************************************/

void dng_stream::DoWrite (const void * /* data */,
						  uint32 /* count */,
						  uint64 /* offset */)
	{
	
	ThrowProgramError ();

	}
		
/*****************************************************************************/

bool dng_stream::BigEndian () const
	{
	
	return fSwapBytes != (!!qDNGBigEndian);
	
	}
		
/*****************************************************************************/

void dng_stream::SetBigEndian (bool bigEndian)
	{
	
	fSwapBytes = (bigEndian != (!!qDNGBigEndian));
	
	}
		
/*****************************************************************************/

const void * dng_stream::Data () const
	{
	
	if (fBufferStart == 0 && fHaveLength && fBufferEnd == fLength)
		{
		
		return fBuffer;
		
		}
		
	return NULL;
	
	}
		
/*****************************************************************************/

dng_memory_block * dng_stream::AsMemoryBlock (dng_memory_allocator &allocator)
	{
	
	Flush ();
	
	uint64 len64 = Length ();
	
	if (len64 > 0xFFFFFFFF)
		{
		ThrowProgramError ();
		}
	
	uint32 len = (uint32) len64;
	
	AutoPtr<dng_memory_block> block (allocator.Allocate (len));
	
	if (len)
		{
	
		SetReadPosition (0);
		
		Get (block->Buffer (), len);
		
		}
		
	return block.Release ();
	
	}
		
/*****************************************************************************/

void dng_stream::SetReadPosition (uint64 offset)
	{
	
	fPosition = offset;
	
	if (fPosition > Length ())
		{
		
		ThrowEndOfFile ();

		}
	
	}

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

uint64 dng_stream::OffsetInOriginalFile () const
	{
	
	return fOffsetInOriginalFile;
	
	}

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

uint64 dng_stream::PositionInOriginalFile () const
	{
	
	if (fOffsetInOriginalFile == kDNGStreamInvalidOffset)
		return kDNGStreamInvalidOffset;
	
	return fOffsetInOriginalFile + Position ();
	
	}

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

void dng_stream::Get (void *data, uint32 count)
	{
	
	while (count)
		{
		
		// See if the request is totally inside buffer.
		
		if (fPosition >= fBufferStart && fPosition + count <= fBufferEnd)
			{
			
			DoCopyBytes (fBuffer + (uint32) (fPosition - fBufferStart),
						 data,
						 count);
						 
			fPosition += count;
			
			return;
			
			}
			
		// See if first part of request is inside buffer.
		
		if (fPosition >= fBufferStart && fPosition < fBufferEnd)
			{
			
			uint32 block = (uint32) (fBufferEnd - fPosition);
			
			DoCopyBytes (fBuffer + (fPosition - fBufferStart),
						 data,
						 block);
			
			count -= block;
			
			data = (void *) (((char *) data) + block);
			
			fPosition += block;
			
			}
			
		// Flush buffer if dirty.
		
		Flush ();
		
		// Do large reads unbuffered.
		
		if (count > fBufferSize)
			{
			
			if (fPosition + count > Length ())
				{
				
				ThrowEndOfFile ();
				
				}
				
			DoRead (data,
					count,
					fPosition);
					
			fPosition += count;
			
			return;
			
			}
			
		// Figure out new buffer range.
		
		fBufferStart = fPosition;
		
		if (fBufferSize >= 4096)
			{
			
			// Align to a 4K file block.
			
			fBufferStart &= (uint64) ~((int64) 4095);
			
			}
		
		fBufferEnd = Min_uint64 (fBufferStart + fBufferSize, Length ());
		
		if (fBufferEnd <= fPosition)
			{
			
			ThrowEndOfFile ();

			}
			
		// Read data into buffer.
		
		dng_abort_sniffer::SniffForAbort (fSniffer);
		
		DoRead (fBuffer,
				(uint32) (fBufferEnd - fBufferStart),
				fBufferStart);
		
		}

	}
		
/*****************************************************************************/

void dng_stream::SetWritePosition (uint64 offset)
	{
	
	fPosition = offset;
	
	}
		
/*****************************************************************************/

void dng_stream::Flush ()
	{
	
	if (fBufferDirty)
		{
		
		dng_abort_sniffer::SniffForAbort (fSniffer);

		DoWrite (fBuffer,
				 (uint32) (fBufferEnd - fBufferStart),
				 fBufferStart);
				 
		fBufferStart = 0;
		fBufferEnd   = 0;
		fBufferLimit = fBufferSize;
		
		fBufferDirty = false;
		
		}

	}

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

void dng_stream::SetLength (uint64 length)
	{
	
	Flush ();
	
	if (Length () != length)
		{
		
		DoSetLength (length);
		
		fLength = length;
		
		}
		
	}
		
/*****************************************************************************/

void dng_stream::Put (const void *data,
					  uint32 count)
	{
	
	// See if we can replace or append to the existing buffer.
	
	uint64 endPosition = fPosition + count;
	
	if (fBufferDirty                &&
		fPosition   >= fBufferStart &&
		fPosition   <= fBufferEnd   &&
		endPosition <= fBufferLimit)
		{
		
		DoCopyBytes (data,
					 fBuffer + (uint32) (fPosition - fBufferStart),
				     count);
				
		if (fBufferEnd < endPosition)
			fBufferEnd = endPosition;
		
		}
		
	// Else we need to write to the file.
		
	else
		{
		
		// Write existing buffer.
		
		Flush ();
		
		// Write large blocks unbuffered.
		
		if (count >= fBufferSize)
			{
			
			dng_abort_sniffer::SniffForAbort (fSniffer);
			
			DoWrite (data, count, fPosition);
			
			}
			
		// Start a new buffer with small blocks.
			
		else
			{
			
			fBufferDirty = true;
			
			fBufferStart = fPosition;
			fBufferEnd   = endPosition;
			fBufferLimit = fBufferStart + fBufferSize;
			
			DoCopyBytes (data,
						 fBuffer,
					     count);
				
			}
		
		}
		
	fPosition = endPosition;
	
	fLength = Max_uint64 (Length (), fPosition);
	
	}
		
/*****************************************************************************/

uint16 dng_stream::Get_uint16 ()
	{
	
	uint16 x;
	
	Get (&x, 2);
	
	if (fSwapBytes)
		{
		
		x = SwapBytes16 (x);
			
		}
	
	return x;
	
	}

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

void dng_stream::Put_uint16 (uint16 x)
	{
	
	if (fSwapBytes)
		{
		
		x = SwapBytes16 (x);
			
		}
		
	Put (&x, 2);
	
	}

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

uint32 dng_stream::Get_uint32 ()
	{
	
	uint32 x;
	
	Get (&x, 4);
	
	if (fSwapBytes)
		{
		
		x = SwapBytes32 (x);
			
		}
	
	return x;
	
	}

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

void dng_stream::Put_uint32 (uint32 x)
	{
	
	if (fSwapBytes)
		{
		
		x = SwapBytes32 (x);
			
		}
		
	Put (&x, 4);
	
	}

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

uint64 dng_stream::Get_uint64 ()
	{
	
	if (fSwapBytes)
		{
	
		union
			{
			uint32 u32 [2];
			uint64 u64;
			} u;
			
		u.u32 [1] = Get_uint32 ();
		u.u32 [0] = Get_uint32 ();
			
		return u.u64;
		
		}
		
	uint64 x;
	
	Get (&x, 8);
	
	return x;
	
	}

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

void dng_stream::Put_uint64 (uint64 x)
	{
	
	if (fSwapBytes)
		{
		
		union
			{
			uint32 u32 [2];
			uint64 u64;
			} u;
			
		u.u64 = x;
			
		Put_uint32 (u.u32 [1]);
		Put_uint32 (u.u32 [0]);
			
		}
		
	else
		{
		
		Put (&x, 8);
		
		}
		
	}

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

real32 dng_stream::Get_real32 ()
	{
	
	union
		{
		uint32 i;
		real32 r;
		} u;
		
	u.i = Get_uint32 ();
	
	return u.r;
	
	}

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

void dng_stream::Put_real32 (real32 x)
	{
	
	if (fSwapBytes)
		{
	
		union
			{
			uint32 i;
			real32 r;
			} u;
			
		u.r = x;
	
		Put_uint32 (u.i);
		
		}
		
	else
		{
		
		Put (&x, 4);
		
		}
	
	}

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

real64 dng_stream::Get_real64 ()
	{
	
	if (fSwapBytes)
		{
	
		union
			{
			uint32 i [2];
			real64 r;
			} u;
			
		u.i [1] = Get_uint32 ();
		u.i [0] = Get_uint32 ();
			
		return u.r;
		
		}
		
	real64 x;
	
	Get (&x, 8);
	
	return x;
	
	}

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

void dng_stream::Put_real64 (real64 x)
	{
	
	if (fSwapBytes)
		{
		
		union
			{
			uint32 i [2];
			real64 r;
			} u;
			
		u.r = x;
			
		Put_uint32 (u.i [1]);
		Put_uint32 (u.i [0]);
			
		}
		
	else
		{
		
		Put (&x, 8);
		
		}
		
	}

/*****************************************************************************/
	
void dng_stream::Get_CString (char *data, uint32 maxLength)
	{

	memset (data, 0, maxLength);
	
	uint32 index = 0;
	
	while (true)
		{
		
		char c = (char) Get_uint8 ();
		
		if (index + 1 < maxLength)
			data [index++] = c;
		
		if (c == 0)
			break;
			
		}
	
	}

/*****************************************************************************/
	
void dng_stream::Get_UString (char *data, uint32 maxLength)
	{
	
	memset (data, 0, maxLength);
	
	uint32 index = 0;
	
	while (true)
		{
		
		char c = (char) Get_uint16 ();
		
		if (index + 1 < maxLength)
			data [index++] = (char) c;
		
		if (c == 0)
			break;
			
		}
	
	}
		
/*****************************************************************************/

void dng_stream::PutZeros (uint64 count)
	{
	
	const uint32 kZeroBufferSize = 4096;
	
	if (count >= kZeroBufferSize)
		{
		
		dng_memory_data zeroBuffer (kZeroBufferSize);
		
		DoZeroBytes (zeroBuffer.Buffer (), 
					 kZeroBufferSize);
		
		while (count)
			{
			
			uint64 blockSize = Min_uint64 (count, kZeroBufferSize);
			
			Put (zeroBuffer.Buffer (), (uint32) blockSize);
			
			count -= blockSize;
			
			}
		
		}
		
	else
		{
		
		uint32 count32 = (uint32) count;
	
		for (uint32 j = 0; j < count32; j++)
			{
			
			Put_uint8 (0);
			
			}
			
		}
	
	}
		
/*****************************************************************************/

void dng_stream::PadAlign2 ()
	{
	
	PutZeros (Position () & 1);
	
	}
		
/*****************************************************************************/

void dng_stream::PadAlign4 ()
	{
	
	PutZeros ((4 - (Position () & 3)) & 3);
	
	}

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

uint32 dng_stream::TagValue_uint32 (uint32 tagType)
	{
	
	switch (tagType)
		{
		
		case ttByte:
			return (uint32) Get_uint8 ();
			
		case ttShort:
			return (uint32) Get_uint16 ();
			
		case ttLong:
		case ttIFD:
			return Get_uint32 ();
		
		}
		
	real64 x = TagValue_real64 (tagType);
	
	if (x < 0.0)
		x = 0.0;
		
	if (x > (real64) 0xFFFFFFFF)
		x = (real64) 0xFFFFFFFF;
		
	return ConvertDoubleToUint32(x + 0.5);
	
	}
	
/*****************************************************************************/

int32 dng_stream::TagValue_int32 (uint32 tagType)
	{
	
	switch (tagType)
		{
		
		case ttSByte:
			return (int32) Get_int8 ();
			
		case ttSShort:
			return (int32) Get_int16 ();
			
		case ttSLong:
			return Get_int32 ();
		
		}
		
	real64 x = TagValue_real64 (tagType);
	
	if (x < 0.0)
		{
		
		if (x < -2147483648.0)
			x = -2147483648.0;
			
		return ConvertDoubleToInt32(x - 0.5);
		
		}
		
	else
		{
		
		if (x > 2147483647.0)
			x = 2147483647.0;
		
		return ConvertDoubleToInt32(x + 0.5);
		
		}
		
	}
	
/*****************************************************************************/

dng_urational dng_stream::TagValue_urational (uint32 tagType)
	{
	
	dng_urational result;
	
	result.n = 0;
	result.d = 1;
	
	switch (tagType)
		{
		
		case ttRational:
			{
		
			result.n = Get_uint32 ();
			result.d = Get_uint32 ();
			
			break;
			
			}
			
		case ttSRational:
			{
			
			int32 n = Get_int32 ();
			int32 d = Get_int32 ();
			
			if ((n < 0) == (d < 0))
				{
			
				if (d < 0)
					{
					result.n = (uint32) ((int64) n * -1);
					result.d = (uint32) ((int64) d * -1);
					}
				else
					{
					result.n = (uint32) n;
					result.d = (uint32) d;
					}
					
				}
				
			break;
			
			}
			
		case ttByte:
		case ttShort:
		case ttLong:
		case ttIFD:
			{
			
			result.n = TagValue_uint32 (tagType);
			
			break;
			
			}
		
		case ttSByte:
		case ttSShort:
		case ttSLong:
			{
			
			int32 n = TagValue_int32 (tagType);
			
			if (n > 0)
				{
				result.n = (uint32) n;
				}
			
			break;
			
			}
			
		default:
			{
			
			real64 x = TagValue_real64 (tagType);
			
			if (x > 0.0)
				{
				
				while (result.d < 10000 && x < 1000000)
					{
					
					result.d *= 10;
					
					x *= 10.0;
					
					}
				
				result.n = ConvertDoubleToUint32(x + 0.5);
				
				}
			
			}
		
		}
		
	return result;
		
	}

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

dng_srational dng_stream::TagValue_srational (uint32 tagType)
	{
	
	dng_srational result;
	
	result.n = 0;
	result.d = 1;
	
	switch (tagType)
		{
		
		case ttSRational:
			{
		
			result.n = Get_int32 ();
			result.d = Get_int32 ();
			
			break;
			
			}
		
		default:
			{
			
			real64 x = TagValue_real64 (tagType);
			
			if (x > 0.0)
				{
				
				while (result.d < 10000 && x < 1000000.0)
					{
					
					result.d *= 10;
					
					x *= 10.0;
					
					}
				
				result.n = ConvertDoubleToInt32(x + 0.5);
				
				}
				
			else
				{
				
				while (result.d < 10000 && x > -1000000.0)
					{
					
					result.d *= 10;
					
					x *= 10.0;
					
					}
				
				result.n = ConvertDoubleToInt32(x - 0.5);
				
				}
			
			}
			
		}
		
	return result;

	}

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

real64 dng_stream::TagValue_real64 (uint32 tagType)
	{
	
	switch (tagType)
		{
		
		case ttByte:
		case ttShort:
		case ttLong:
		case ttIFD:
			return (real64) TagValue_uint32 (tagType);
			
		case ttSByte:
		case ttSShort:
		case ttSLong:
			return (real64) TagValue_int32 (tagType);
			
		case ttRational:
			{
			
			uint32 n = Get_uint32 ();
			uint32 d = Get_uint32 ();
			
			if (d == 0)
				return 0.0;
			else
				return (real64) n / (real64) d;
				
			}
		
		case ttSRational:
			{
			
			int32 n = Get_int32 ();
			int32 d = Get_int32 ();
			
			if (d == 0)
				return 0.0;
			else
				return (real64) n / (real64) d;
				
			}
			
		case ttFloat:
			return (real64) Get_real32 ();
			
		case ttDouble:
			return Get_real64 ();
			
		}
		
	return 0.0;

	}
		
/*****************************************************************************/

void dng_stream::CopyToStream (dng_stream &dstStream,
							   uint64 count)
	{
	
	uint8 smallBuffer [1024];
		
	if (count <= sizeof (smallBuffer))
		{
		
		Get (smallBuffer, (uint32) count);
		
		dstStream.Put (smallBuffer, (uint32) count);
		
		}
		
	else
		{
	
		const uint32 bigBufferSize = (uint32) Min_uint64 (kBigBufferSize,
													      count);
		
		dng_memory_data bigBuffer (bigBufferSize);
		
		while (count)
			{
			
			uint32 blockCount = (uint32) Min_uint64 (bigBufferSize,
													 count);
													 
			Get (bigBuffer.Buffer (),
				 blockCount);
			
			dstStream.Put (bigBuffer.Buffer (),
						   blockCount);
						   
			count -= blockCount;
			
			}
	
		}

	}
		
/*****************************************************************************/

void dng_stream::DuplicateStream (dng_stream &dstStream)
	{
	
	// Turn off sniffers for this operation.
	
	TempStreamSniffer noSniffer1 (*this    , NULL);
	TempStreamSniffer noSniffer2 (dstStream, NULL);
		
	// First grow the destination stream if required, in an attempt to
	// reserve any needed space before overwriting the existing data.
	
	if (dstStream.Length () < Length ())
		{
		dstStream.SetLength (Length ());
		}
		
	SetReadPosition (0);
	
	dstStream.SetWritePosition (0);
	
	CopyToStream (dstStream, Length ());
	
	dstStream.Flush ();
							
	dstStream.SetLength (Length ());

	}
		
/*****************************************************************************/

TempBigEndian::TempBigEndian (dng_stream &stream,
						 	  bool bigEndian)
	
	:	fStream  (stream)
	,	fOldSwap (stream.SwapBytes ())
	
	{
	
	fStream.SetBigEndian (bigEndian);
	
	}
		
/*****************************************************************************/

TempBigEndian::~TempBigEndian ()
	{
	
	fStream.SetSwapBytes (fOldSwap);
	
	}
		
/*****************************************************************************/

TempStreamSniffer::TempStreamSniffer (dng_stream &stream,
									  dng_abort_sniffer *sniffer)
	
	:	fStream     (stream)
	,	fOldSniffer (stream.Sniffer ())
	
	{
	
	fStream.SetSniffer (sniffer);
	
	}
		
/*****************************************************************************/

TempStreamSniffer::~TempStreamSniffer ()
	{
	
	fStream.SetSniffer (fOldSniffer);
	
	}
		
/*****************************************************************************/