www.pudn.com > pueblo.zip > ChArch.cpp
/*----------------------------------------------------------------------------
_ _ _
/\ | | | (_)
/ \ _ __ __| |_ __ ___ _ __ ___ ___ __| |_ __ _
/ /\ \ | '_ \ / _` | '__/ _ \| '_ ` _ \ / _ \/ _` | |/ _` |
/ ____ \| | | | (_| | | | (_) | | | | | | __/ (_| | | (_| |
/_/ \_\_| |_|\__,_|_| \___/|_| |_| |_|\___|\__,_|_|\__,_|
The contents of this file are subject to the Andromedia Public
License Version 1.0 (the "License"); you may not use this file
except in compliance with the License. You may obtain a copy of
the License at http://www.andromedia.com/APL/
Software distributed under the License is distributed on an
"AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
implied. See the License for the specific language governing
rights and limitations under the License.
The Original Code is Pueblo client code, released November 4, 1998.
The Initial Developer of the Original Code is Andromedia Incorporated.
Portions created by Andromedia are Copyright (C) 1998 Andromedia
Incorporated. All Rights Reserved.
Andromedia Incorporated 415.365.6700
818 Mission Street - 2nd Floor 415.365.6701 fax
San Francisco, CA 94103
Contributor(s):
--------------------------------------------------------------------------
Chaco team: Dan Greening, Glenn Crocker, Jim Doubek,
Coyote Lussier, Pritham Shetty.
Wrote and designed original codebase.
------------------------------------------------------------------------------
This file consists of the implementation of the ChArchive class.
----------------------------------------------------------------------------*/
// $Header: /home/cvs/chaco/api/ChArch.cpp,v 2.1 1995/07/26 08:33:50 coyote Exp $
#include "headers.h"
#include
#include
#if defined( CH_MSW ) && defined( CH_ARCH_16 )
#include
#endif
#if defined( CH_UNIX )
#include
#include
#include
#include
#define ASSERT_VALID( object )
#endif // defined( CH_UNIX )
// Minimum buffer size
enum { sBufSizeMin = 128 };
static chuint32 ReadStringLength( ChArchive &ar );
/*----------------------------------------------------------------------------
Types
----------------------------------------------------------------------------*/
/* This struct is for composing
and decomposing a float */
typedef struct
{
chuint8 buFloatBits[sizeof( float )];
} FloatParts;
/*----------------------------------------------------------------------------
ChArchive constructor and destructor
----------------------------------------------------------------------------*/
ChArchive::ChArchive( ChData *pData, ChDataMode mode, chflag16 fOptions )
{
chint32 lBufSize = 512;
ASSERT_VALID( pData );
ASSERT( modeRead == mode || modeWrite == mode );
/* Initialize members not
dependent on allocated
buffer */
m_mode = mode;
m_fOptions = fOptions;
m_pData = pData;
/* Initialize the buffer. Minimum
size is 128. */
m_boolUserBuf = true;
m_boolDirectBuffer = false;
m_lBufSize = sBufSizeMin;
m_pBufStart = 0;
lBufSize = m_lBufSize;
if (0 == m_pBufStart)
{ /* check for ChData providing
buffering support */
m_boolDirectBuffer = (bool)m_pData->GetBufferPtr( ChData::bufferCheck );
if (!m_boolDirectBuffer)
{ /* No support for direct buffering,
so allocate a new buffer */
m_pBufStart = new chuint8[m_lBufSize];
m_boolUserBuf = false;
}
else
{ /* This ChData * supports direct
buffering! */
lBufSize = 0; // Will trigger initial FillBuffer
}
}
if (!m_boolDirectBuffer)
{
ASSERT( m_pBufStart != 0);
}
m_pBufMax = m_pBufStart + lBufSize;
m_pBufCur = (GetMode() & modeRead) ? m_pBufMax : m_pBufStart;
// Seek to the start of ChData
m_pData->Seek( ChData::begin, 0 );
}
ChArchive::~ChArchive()
{
Abort(); // abort completely shuts down the archive
}
/*----------------------------------------------------------------------------
ChArchive public methods
----------------------------------------------------------------------------*/
ChDataMode ChArchive::GetMode() const
{
return m_mode;
}
ChData* ChArchive::GetData() const
{
return m_pData;
}
void ChArchive::Abort()
{
ASSERT( m_boolDirectBuffer || 0 != m_pBufStart );
/* Close sets m_pData to 0. If it
is not 0, we must Close the
ChArchive */
if (0 != m_pData && !(m_fOptions & optNoFlushOnDelete))
{
Close();
}
if (!m_boolUserBuf)
{
ASSERT( !m_boolDirectBuffer );
delete[] m_pBufStart;
m_pBufStart = 0;
}
}
void ChArchive::Close()
{
ASSERT_VALID( m_pData );
Flush();
m_pData = 0;
}
void ChArchive::Flush()
{
ASSERT_VALID( m_pData );
ASSERT( m_boolDirectBuffer || 0 != m_pBufStart );
ASSERT( m_boolDirectBuffer || 0 != m_pBufCur );
if (GetMode() & modeRead)
{
// unget the characters in the buffer, seek back unused amount
m_pData->Seek( -(m_pBufMax - m_pBufCur), ChData::current );
m_pBufCur = m_pBufMax; // empty
}
else
{
if (0 == m_pBufStart || m_pBufCur != m_pBufStart)
{
if (!m_boolDirectBuffer)
{ /* write out the current buffer to
the ChData object */
m_pData->Write( m_pBufStart, m_pBufCur - m_pBufStart );
}
else
{ // Commit the current buffer
m_pData->GetBufferPtr( ChData::bufferCommit, m_pBufCur - m_pBufStart );
// Get the next buffer
VERIFY( m_pData->GetBufferPtr( ChData::bufferWrite, m_lBufSize,
(void **)&m_pBufStart,
(void **)&m_pBufMax) ==
(chuint32)m_lBufSize );
ASSERT( m_lBufSize == (chint32)(m_pBufMax - m_pBufStart) );
}
m_pBufCur = m_pBufStart;
}
}
}
/*----------------------------------------------------------------------------
ChArchive reading methods
----------------------------------------------------------------------------*/
ChArchive& ChArchive::operator >>( ChStreamable& streamable )
{
streamable.Serialize( *this );
return *this;
}
ChArchive& ChArchive::operator >>( bool& boolVal )
{
return operator >>( (chuint8 &)boolVal );
}
ChArchive& ChArchive::operator >>( chuint8& buVal )
{
ASSERT( 1 == sizeof( chuint8) ); /* If this isn't true, we're
broken */
if (m_pBufCur + 1 > m_pBufMax)
{ /* There is not enough data in
the buffer */
FillBuffer( 1 - (chuint32)(m_pBufMax - m_pBufCur) );
}
buVal = *m_pBufCur; // Read the value
m_pBufCur++; // Move the buffer pointer
return *this;
}
ChArchive& ChArchive::operator >>( chint8& bVal )
{
ASSERT( sizeof( chuint8 ) == sizeof( chint8 ) );
return operator >>( (chuint8 &)bVal );
}
ChArchive& ChArchive::operator >>( chuint16& suVal )
{
if (m_pBufCur + sizeof( chuint16 ) > m_pBufMax)
{
/* There is not enough data in
the buffer */
FillBuffer( sizeof( chuint16 ) - (chuint32)(m_pBufMax - m_pBufCur) );
}
memcpy( &suVal, m_pBufCur, sizeof( chuint16 ) );
m_pBufCur += sizeof( chuint16 ); // Move the buffer pointer
if (!(m_fOptions & optHostByteOrder))
{ /* Switch back from network byte
order */
suVal = ntohs( suVal );
}
return *this;
}
ChArchive& ChArchive::operator >>( chint16& sVal )
{
ASSERT( sizeof( chuint16 ) == sizeof(chint16 ) );
return operator >>( (chuint16 &)sVal );
}
ChArchive& ChArchive::operator >>( chuint32& luVal )
{
if (m_pBufCur + sizeof( chuint32 ) > m_pBufMax)
{
/* There is not enough data in
the buffer */
FillBuffer( sizeof( chuint32 ) - (chuint32)(m_pBufMax - m_pBufCur) );
}
memcpy( &luVal, m_pBufCur, sizeof( chuint32 ) ); // Read the value
m_pBufCur += sizeof( chuint32 ); // Move the buffer pointer
if (!(m_fOptions & optHostByteOrder))
{ /* Switch back from network byte
order */
luVal = ntohl( luVal );
}
return *this;
}
ChArchive& ChArchive::operator >>( chint32& lVal )
{
ASSERT( sizeof( chuint32 ) == sizeof( chint32 ) );
return operator >>( (chuint32 &)lVal );
}
ChArchive& ChArchive::operator >>( string& strVal )
{
int iConvert;
chuint32 luNewLen;
#if defined( _UNICODE )
{
iConvert = 1; // If we get ANSI, convert
}
#else // defined( _UNICODE )
{
iConvert = 0; // If we get UNICODE, convert
}
#endif // defined( _UNICODE )
luNewLen = ReadStringLength( *this );
if ((chuint32)-1 == luNewLen)
{
iConvert = 1 - iConvert;
luNewLen = ReadStringLength( *this );
ASSERT( luNewLen != -1 ); // We shouldn't get two UNICODEs
}
#if defined( CH_MSW )
{ /* The two versions of 'string'
are so different in the
subtleties that we have two
implementations of the read
operation... */
/* Set length of string to new
length */
chuint32 luByteLen = luNewLen;
#if defined( _UNICODE )
{
strVal.GetBufferSetLength( (int)luByteLen );
luByteLen += luByteLen * (1 - nConvert); // bytes to read
}
#else // defined( _UNICODE )
{
luByteLen += luByteLen * iConvert; // bytes to read
strVal.GetBufferSetLength( (int)luByteLen );
}
#endif // defined( _UNICODE )
// Read in the characters
if (luNewLen != 0)
{
char *pBuffer;
ASSERT( 0 != luByteLen );
// Read new data
pBuffer = strVal.GetBuffer( (int)luByteLen );
if (Read( pBuffer, luByteLen) != luByteLen)
{
/* Throw an end-of-file
exception */
#if defined( CH_EXCEPTIONS )
{
#if defined( CH_MSW) && defined( CH_ARCH_16 )
{
THROW( new ChArchiveEx( ChEx::endOfData ) );
}
#else
throw ChArchiveEx( ChEx::endOfData );
#endif
}
#else // defined( CH_EXCEPTIONS )
{
strVal = "";
return *this;
}
#endif // defined( CH_EXCEPTIONS )
}
pBuffer[luByteLen] = 0; // Zero-terminate the buffer
}
strVal.ReleaseBuffer();
}
#elif defined( CH_UNIX )
{
char *pBuffer;
chuint32 luByteLen = luNewLen;
/* Allocate a buffer for the
string */
pBuffer = new char[luByteLen + 1];
if (Read( pBuffer, luByteLen) != luByteLen)
{
/* Throw an end-of-file
exception */
#if defined( CH_EXCEPTIONS )
{
throw ChArchiveEx( ChEx::endOfData );
}
#else // defined( CH_EXCEPTIONS )
{
strVal = "";
return *this;
}
#endif // defined( CH_EXCEPTIONS )
}
pBuffer[luByteLen] = 0; // Zero-terminate the buffer
strVal = pBuffer; // Convert the buffer to a string
delete[] pBuffer;
}
#endif
return *this;
}
static chuint32 ReadStringLength( ChArchive &archive )
{
chuint32 luNewLen;
chuint8 buLen;
chuint16 suLen;
// First try to read a byte length
archive >> buLen;
if (buLen < 0xff)
{
return buLen;
}
// Now try to read a 16-bit length
archive >> suLen;
if (suLen == 0xfffe)
{ /* UNICODE string prefix
(length will follow) */
return (chuint32)-1;
}
else if (suLen == 0xffff)
{ // This is a 32-bit length
archive >> luNewLen;
return luNewLen;
}
else
{
return suLen;
}
}
ChArchive& ChArchive::operator >>( float& fVal )
{
FloatParts parts;
if (m_pBufCur + sizeof( float ) > m_pBufMax)
{
/* There is not enough data in
the buffer */
FillBuffer( sizeof( float ) - (chuint32)(m_pBufMax - m_pBufCur) );
}
*(FloatParts*)&fVal = *(FloatParts*)m_pBufCur;
m_pBufCur += sizeof( float );
*(float*)&parts = fVal;
ASSERT( sizeof( float ) == 4 );
(*(FloatParts*)&fVal).buFloatBits[0] = parts.buFloatBits[3];
(*(FloatParts*)&fVal).buFloatBits[1] = parts.buFloatBits[2];
(*(FloatParts*)&fVal).buFloatBits[2] = parts.buFloatBits[1];
(*(FloatParts*)&fVal).buFloatBits[3] = parts.buFloatBits[0];
return *this;
}
/*----------------------------------------------------------------------------
ChArchive writing methods
----------------------------------------------------------------------------*/
ChArchive& ChArchive::operator <<( ChStreamable &streamable )
{
streamable.Serialize( *this );
return *this;
}
ChArchive& ChArchive::operator <<( bool boolVal )
{
boolVal = !!boolVal; // Ensure it's boolean
return operator <<( (chuint8)boolVal );
}
ChArchive& ChArchive::operator <<( chuint8 buVal )
{
ASSERT( 1 == sizeof( chuint8) ); /* If this isn't true, we're
broken */
if (m_pBufCur + 1 > m_pBufMax)
{
Flush();
}
*m_pBufCur = buVal;
m_pBufCur++;
return( *this );
}
ChArchive& ChArchive::operator <<( chint8 bVal )
{
ASSERT( sizeof( chuint8 ) == sizeof( chint8 ) );
return operator <<( (chuint8)bVal );
}
ChArchive& ChArchive::operator <<( chuint16 suVal )
{
if (m_pBufCur + sizeof( chuint16 ) > m_pBufMax)
{
Flush();
}
chuint16 sTemp;
if (!(m_fOptions & optHostByteOrder))
{ // Switch to network byte order
sTemp = htons( suVal );
}
else
{ // No byte swapping
sTemp = suVal;
}
memcpy( m_pBufCur, &sTemp, sizeof( chuint16 ) );
m_pBufCur += sizeof( chuint16 );
return( *this );
}
ChArchive& ChArchive::operator <<( chint16 sVal )
{
ASSERT( sizeof( chuint16 ) == sizeof( chint16 ) );
return operator <<( (chuint16)sVal );
}
ChArchive& ChArchive::operator <<( chuint32 luVal )
{
if (m_pBufCur + sizeof( chuint32 ) > m_pBufMax)
{
Flush();
}
chuint32 luTemp;
if (!(m_fOptions & optHostByteOrder))
{ // Switch to network byte order
luTemp = htonl( luVal );
}
else
{ // No byte swapping
luTemp = luVal;
}
memcpy( m_pBufCur, &luTemp, sizeof( chuint32 ) );
m_pBufCur += sizeof( chuint32 );
return( *this );
}
ChArchive& ChArchive::operator <<( chint32 lVal )
{
ASSERT( sizeof( chuint32 ) == sizeof( chint32 ) );
return operator <<( (chuint32)lVal );
}
/*----------------------------------------------------------------------------
Strings are prefixed in the archive as follows:
byte: |_1____|_2____|_3____|______________________________
| < FF |................Characters (up to 255)
| FF | < FFFE |..Characters (256 - 65534)
| FF | FF | FF |..4 bytes of length followed by characters
| FF | FF | FE |..Special prefix for UNICODE strings,
| | | | followed by normal string encoding
----------------------------------------------------------------------------*/
ChArchive& ChArchive::operator <<( string strVal )
{
chint32 lStringLen;
chint32 lByteLen;
const char *pBuffer;
#if defined( CH_MSW )
{
lStringLen = strVal.GetLength();
}
#else
{
lStringLen = strVal.length();
}
#endif
#if defined( _UNICODE )
{
/* Special signature to recognize
unicode strings */
operator << (chuint8)0xff;
operator << (chuint16)0xfffe;
lByteLen = lStringLen * sizeof( wchar_t );
}
#else // defined( _UNICODE )
{
lByteLen = lStringLen * sizeof( char );
}
#endif // defined( _UNICODE )
if (lStringLen < 0xFF)
{
*this << (chuint8)lStringLen;
}
else if (lStringLen < 0xfffe)
{
*this << (chuint8)0xff;
*this << (chuint16)lStringLen;
}
else
{
*this << (chuint8)0xff;
*this << (chuint16)0xffff;
*this << (chuint32)lStringLen;
}
#if (CH_MSW)
{
pBuffer = strVal.GetBuffer( 1 );
}
#elif defined( CH_UNIX )
{
pBuffer = strVal.chars();
}
#endif
Write( pBuffer, lByteLen );
return *this;
}
ChArchive& ChArchive::operator <<( float fVal )
{
FloatParts parts;
chuint8* pByte;
if (m_pBufCur + sizeof( chuint16 ) > m_pBufMax)
{
Flush();
}
*(float*)&parts = fVal;
ASSERT( sizeof( float ) == 4 );
pByte = m_pBufCur;
*pByte++ = parts.buFloatBits[3];
*pByte++ = parts.buFloatBits[2];
*pByte++ = parts.buFloatBits[1];
*pByte = parts.buFloatBits[0];
m_pBufCur += sizeof( float );
return( *this );
}
/*----------------------------------------------------------------------------
ChArchive public methods (should not be used by module authors)
----------------------------------------------------------------------------*/
void ChArchive::FillBuffer( chuint32 luBytesNeeded )
{
ASSERT_VALID( m_pData );
ASSERT( GetMode() & modeRead );
ASSERT( m_boolDirectBuffer || 0 != m_pBufStart );
ASSERT( m_boolDirectBuffer || 0 != m_pBufCur );
ASSERT( luBytesNeeded > 0 );
ASSERT( luBytesNeeded <= (chuint32)m_lBufSize );
/* Fill up the current buffer from
the ChData object */
if (!m_boolDirectBuffer)
{
ASSERT( 0 != m_pBufCur );
ASSERT( 0 != m_pBufStart );
ASSERT( 0 != m_pBufMax );
if (m_pBufCur > m_pBufStart)
{ /* Move the currently unused part of
the buffer to the start of the
buffer */
chuint32 luUnused = m_pBufMax - m_pBufCur;
chuint32 luActual;
if ((chint32)luUnused > 0)
{
ChMemMove( m_pBufStart, m_pBufCur, luUnused );
m_pBufCur = m_pBufStart;
m_pBufMax -= luUnused;
}
luActual = m_pData->Read( m_pBufStart + luUnused,
m_lBufSize - luUnused );
m_pBufCur = m_pBufStart;
m_pBufMax = m_pBufStart + luUnused + luActual;
}
}
else
{ /* Seek to the unused portion of
the buffer and get the buffer
starting there */
chuint32 luActual;
m_pData->Seek( -(m_pBufMax - m_pBufCur), ChData::current );
luActual = m_pData->GetBufferPtr( ChData::bufferRead, m_lBufSize,
(void **)&m_pBufStart,
(void **)&m_pBufMax );
ASSERT( luActual == (chuint32)(m_pBufMax - m_pBufStart) );
m_pBufCur = m_pBufStart;
}
// Not enough data to fill request?
if ((chuint32)(m_pBufMax - m_pBufCur) < luBytesNeeded)
{
/* Throw an end-of-file
exception */
#if defined( CH_EXCEPTIONS )
{
#if defined( CH_MSW) && defined( CH_ARCH_16 )
{
THROW( new ChArchiveEx( ChEx::endOfData ) );
}
#else
throw ChArchiveEx( ChEx::endOfData );
#endif
}
#endif // defined( CH_EXCEPTIONS )
}
}
chuint32 ChArchive::Read( void *pBuffer, chuint32 luMax )
{
chuint32 luAmtToRead;
chuint32 luTemp;
ASSERT_VALID( m_pData );
if (luMax == 0)
{ // Nothing to read
return 0;
}
ASSERT( 0 != pBuffer );
ASSERT( m_boolDirectBuffer || 0 != m_pBufStart );
ASSERT( m_boolDirectBuffer || 0 != m_pBufCur );
ASSERT( GetMode() & modeRead );
/* Try to fill from the buffer
first */
luAmtToRead = luMax;
/* Figure out what the maximum
we can read from the buffer
is (this is zero if there is
no buffer */
luTemp = min( luAmtToRead, (chuint32)(m_pBufMax - m_pBufCur) );
/* Copy the maximum number of bytes
available (no op for non-
buffered) */
ChMemCopy( pBuffer, m_pBufCur, luTemp );
m_pBufCur += luTemp; // Move the buffer pointer
pBuffer = (chuint8 *)pBuffer + luTemp; /* Move the start of the read
buffer */
luAmtToRead -= luTemp; // Adjust the amount still to read
if (luAmtToRead != 0)
{ // We still have to read data
chuint32 luRead;
ASSERT( m_pBufCur == m_pBufMax );
/* Read the rest in buffer-size
chunks */
luTemp = luAmtToRead - (luAmtToRead % m_lBufSize);
luRead = m_pData->Read( pBuffer, luTemp );
pBuffer = (chuint8 *)pBuffer + luRead;
luAmtToRead -= luRead;
/* Read the last (odd-sized) chunk
into buffer and then copy */
if (luRead == luTemp)
{
ASSERT( m_pBufCur == m_pBufMax );
ASSERT( luAmtToRead < (chuint32)m_lBufSize );
/* Fill buffer (similar to
ChArchive::FillBuffer, but no
exception) */
if (!m_boolDirectBuffer)
{ // Read from ChData's buffer
luRead = m_pData->Read( m_pBufStart,
max( luAmtToRead,
(chuint32)m_lBufSize ) );
m_pBufCur = m_pBufStart;
m_pBufMax = m_pBufStart + luRead;
}
else
{ // Read from ChData directly
luRead = m_pData->GetBufferPtr( ChData::bufferRead, m_lBufSize,
(void **)&m_pBufStart,
(void **)&m_pBufMax );
ASSERT( luRead == (chuint32)(m_pBufMax - m_pBufStart) );
m_pBufCur = m_pBufStart;
}
// use first part for rest of read
luTemp = min( luAmtToRead, (chuint32)(m_pBufMax - m_pBufCur) );
ChMemCopy( pBuffer, m_pBufCur, luTemp );
m_pBufCur += luTemp;
luAmtToRead -= luTemp;
}
}
/* Return the amount desired minus
the amount we still need to
read */
return luMax - luAmtToRead;
}
void ChArchive::Write( const void* pBuffer, chuint32 luMax )
{
chuint32 luTemp;
ASSERT_VALID( m_pData );
if (0 == luMax)
{ // Nothing to write
return;
}
ASSERT( pBuffer != 0 );
ASSERT( m_boolDirectBuffer || 0 != m_pBufStart );
ASSERT( m_boolDirectBuffer || 0 != m_pBufCur);
ASSERT( GetMode() & modeWrite );
// Copy to buffer if possible
luTemp = min( luMax, (chuint32)(m_pBufMax - m_pBufCur) );
ChMemCopy( m_pBufCur, pBuffer, luTemp );
m_pBufCur += luTemp; // Advance the write buffer ptr
pBuffer = (chuint8 *)pBuffer + luTemp;
luMax -= luTemp;
if (luMax > 0)
{ // There is still data to write
Flush(); // Flush the full buffer
/* Write rest of the buffer in
buffer-sized chunks */
luTemp = luMax - (luMax % m_lBufSize);
m_pData->Write( pBuffer, luTemp );
pBuffer = (chuint8 *)pBuffer + luTemp;
luMax -= luTemp;
if (m_boolDirectBuffer)
{ /* Adjust the direct buffer to the
new file position */
VERIFY( m_pData->GetBufferPtr( ChData::bufferWrite, m_lBufSize,
(void **)&m_pBufStart,
(void **)&m_pBufMax) ==
(chuint32)m_lBufSize );
ASSERT( m_lBufSize == (chint32)(m_pBufMax - m_pBufStart) );
m_pBufCur = m_pBufStart;
}
/* Copy the remaining data to the
active buffer */
ASSERT( luMax < (chuint32)m_lBufSize );
ASSERT( m_pBufCur == m_pBufStart );
ChMemCopy( m_pBufCur, pBuffer, luMax );
m_pBufCur += luMax;
}
}