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