/*****************************************************************************/
// 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);
}
/*****************************************************************************/