//+--------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1992 - 2000. // // File: RCSTRMIT.HXX // // Contents: Persistent Recoverable Stream Iterators for fixed // size records. // // Classes: CRcovStrmReadIter // CRcovStrmWriteIter // CRcovStrmAppendIter // // History: 25-Jan-94 SrikantS Created. // //---------------------------------------------------------------------------- #pragma once #include //+--------------------------------------------------------------------------- // // Class: CRcovStrmIter // // Purpose: Iterator for the recoverable stream class assuming fixed // size records are stored in the stream. // // History: 1-25-94 srikants Created // //---------------------------------------------------------------------------- class CRcovStrmIter { public: inline CRcovStrmIter( CRcovStrmTrans & trans , USHORT cbRec ); inline BOOL GetRec( void *pvRec, ULONG iRec ); inline void GetRec( void *pvRec ); inline ULONG GetVariableRecSize(); inline void GetVariableRecData( void * pvRec, ULONG cbRec ); BOOL AtEnd() const { return _trans.AtEnd(); } inline void Advance(); inline void Seek( ULONG iRec ); ULONG UserDataSize( ULONG cRec ) { return cRec * (_cbRec + _CHECKSUM_SIZE); } ULONG UserRecordCount( ULONG cbUserData ) { return cbUserData / (_cbRec + _CHECKSUM_SIZE); } static unsigned SizeofChecksum() { return _CHECKSUM_SIZE; } protected: enum { _CHECKSUM_SIZE = sizeof(ULONG) }; inline ULONG CheckSum( void const * pvRec ) const; inline ULONG CheckSum( void const * pvRec, unsigned cbRec ) const; CRcovStrmTrans & _trans; // Transaction context const USHORT _cbRec; // Size of user record, sans checksum }; //+--------------------------------------------------------------------------- // // Method: CRcovStrmIter::CRcovStrmIter, public // // Synopsis: Constructor. Just squirrels away member variables. // // Arguments: [trans] -- Transaction context to iterate // [cbRec] -- Size of 'records' to be written. // // History: 27-May-1998 KyleP Added header // //---------------------------------------------------------------------------- inline CRcovStrmIter::CRcovStrmIter( CRcovStrmTrans & trans , USHORT cbRec ) : _trans( trans ), _cbRec(cbRec) { } //+--------------------------------------------------------------------------- // // Method: CRcovStrmIter::Advance, public // // Synopsis: Moves to next record // // History: 27-May-1998 KyleP Added header // //---------------------------------------------------------------------------- inline void CRcovStrmIter::Advance() { Win4Assert( !AtEnd() ); _trans.Advance( _cbRec + _CHECKSUM_SIZE ); } //+--------------------------------------------------------------------------- // // Method: CRcovStrmIter::Seek, public // // Synopsis: Moves to specified record // // Arguments: [iRec] -- Record to position to. // // History: 27-May-1998 KyleP Created // //---------------------------------------------------------------------------- inline void CRcovStrmIter::Seek( ULONG iRec ) { _trans.Seek( iRec * (_cbRec + _CHECKSUM_SIZE) ); } //+--------------------------------------------------------------------------- // // Method: CRcovStrmIter::CheckSum, protected // // Synopsis: Computes checksum for record // // Arguments: [pvRec] -- Record to examine // // Returns: Checksum // // History: 27-May-1998 KyleP Created // // Notes: Checksum is never 0. This is to avoid a common failed write // scenario where zeros are written (or nothing is ever written) // to a persistent data structure. // //---------------------------------------------------------------------------- inline ULONG CRcovStrmIter::CheckSum( void const * pvRec ) const { return CheckSum( pvRec, _cbRec ); } //+--------------------------------------------------------------------------- // // Method: CRcovStrmIter::CheckSum, protected // // Synopsis: Computes checksum for variable size records // // Arguments: [pvRec] -- Record to examine // [cbRec] -- Size of record // // Returns: Checksum // // History: 1-Jun-1998 VikasMan Created // // Notes: Checksum is never 0. This is to avoid a common failed write // scenario where zeros are written (or nothing is ever written) // to a persistent data structure. // //---------------------------------------------------------------------------- inline ULONG CRcovStrmIter::CheckSum( void const * pvRec, unsigned cbRec ) const { BYTE * pb = (BYTE *)pvRec; ULONG ulCheckSum = 0; // // First, the DWORD granular portion // unsigned cbEnd = cbRec - _CHECKSUM_SIZE; for ( unsigned i = 0; i <= cbEnd; i += _CHECKSUM_SIZE ) { ulCheckSum += *(ULONG UNALIGNED *)pb; pb += _CHECKSUM_SIZE; } // // Then whatever is left. // ULONG ul = 0; for ( ; i < cbRec; i++ ) { ul = (ul << 8) + *pb; pb++; } ulCheckSum += ul; // // Make sure the checksum is never zero. Just to avoid an obvious case // of error where a whole section has been zero-ed (including checksum). // if ( 0 == ulCheckSum ) ulCheckSum = 1; return ulCheckSum; } //+--------------------------------------------------------------------------- // // Function: CRcovStrmIter::GetRec, public // // Synopsis: Gets the specified record. // // Effects: Positions the current pointer after the current read. // // Arguments: [pvRec] -- Buffer to read the contents into. // [iRec] -- Number of the record, starting at 0. // // Returns: TRUE if a record was successfully read. FALSE if iRec // is beyond the end of stream. // // History: 2-10-94 srikants Created // //---------------------------------------------------------------------------- inline BOOL CRcovStrmIter::GetRec( void * pvRec , ULONG iRec ) { if ( !_trans.Seek(iRec * (_cbRec + _CHECKSUM_SIZE) ) || AtEnd() ) { return FALSE; } ULONG cbRead = _trans.Read( pvRec, _cbRec ); Win4Assert( cbRead == _cbRec ); ULONG ulCheckSum; if ( _CHECKSUM_SIZE != _trans.Read( &ulCheckSum, sizeof(ulCheckSum) ) ) { ciDebugOut(( DEB_ERROR, "Error reading checksum for record %u (cbRec = %u).\n", iRec, _cbRec )); Win4Assert( !"Bad checksum" ); THROW( CException(CI_CORRUPT_CATALOG) ); } if ( ulCheckSum != CheckSum( pvRec ) ) { ciDebugOut(( DEB_ERROR, "Incorrect checksum 0x%x (should be 0x%x) for record %u (cbRec = %u). pvRec = 0x%x\n", CheckSum( pvRec ), ulCheckSum, iRec, _cbRec, pvRec )); Win4Assert( !"Bad checksum" ); THROW( CException(CI_CORRUPT_CATALOG) ); } return cbRead == _cbRec; } //+--------------------------------------------------------------------------- // // Function: CRcovStrmIter::GetRec, public // // Synopsis: Gets the current record. // // Effects: Positions the current pointer after the current read. // // Arguments: [pvRec] -- Buffer to read the contents into. // // History: 27-May-1998 KyleP Created // //---------------------------------------------------------------------------- inline void CRcovStrmIter::GetRec( void *pvRec ) { GetVariableRecData( pvRec, _cbRec ); } //+--------------------------------------------------------------------------- // // Function: CRcovStrmIter::GetVariableRecSize, public // // Synopsis: Gets the size of the current variable record. // // Effects: Positions the current pointer after the current read. // // Arguments: // // Returns: The size in bytes of the records // // History: 1-Jun-1998 VikasMan Created // //---------------------------------------------------------------------------- inline ULONG CRcovStrmIter::GetVariableRecSize() { Win4Assert( !AtEnd() ); ULONG cbRec; ULONG cbRead; // Read Size cbRead = _trans.Read( &cbRec, sizeof( cbRec ) ); Win4Assert( sizeof( cbRec ) == cbRead ); Win4Assert( cbRec ); return cbRec; } //+--------------------------------------------------------------------------- // // Function: CRcovStrmIter::GetVariableRecData, public // // Synopsis: Gets the current variable record. // // Effects: Positions the current pointer after the current read. // // Arguments: [pvRec] -- Pointer to buffer to read the contents into. // [cbRec] -- Size in bytes of pvRec // // // History: 1-Jun-1998 VikasMan Created // //---------------------------------------------------------------------------- inline void CRcovStrmIter::GetVariableRecData( void * pvRec, ULONG cbRec ) { Win4Assert( !AtEnd() ); ULONG cbRead = _trans.Read( pvRec, cbRec ); Win4Assert( cbRead == cbRec ); ULONG ulCheckSum; if ( _CHECKSUM_SIZE != _trans.Read( &ulCheckSum, sizeof(ulCheckSum) ) ) { ciDebugOut(( DEB_ERROR, "Error reading checksum.\n" )); Win4Assert( !"Bad checksum" ); THROW( CException(CI_CORRUPT_CATALOG) ); } if ( ulCheckSum != CheckSum( pvRec, cbRec ) ) { ciDebugOut(( DEB_ERROR, "Incorrect checksum 0x%x (should be 0x%x). pvRec = 0x%x\n", CheckSum( pvRec ), ulCheckSum, pvRec )); Win4Assert( !"Bad checksum" ); THROW( CException(CI_CORRUPT_CATALOG) ); } } #if 0 // untested unsused new function //+--------------------------------------------------------------------------- // // Function: CRcovStrmIter::GetVariableRec, public // // Synopsis: Gets the current variable record. In the process would also // allocate memory for it // // Effects: Positions the current pointer after the current read. // // Arguments: [ppvRec] -- Pointer to buffer to read the contents into. // // Returns: The size in bytes of *ppvRec // // History: 1-Jun-1998 VikasMan Created // //---------------------------------------------------------------------------- inline ULONG CRcovStrmIter::GetVariableRec( void ** ppvRec ) { Win4Assert( !AtEnd() ); *ppvRec = NULL; ULONG cbRec; // Read Size cbRec = GetVariableRecSize(); // Now allocate and read data *ppvRec = (void*) new BYTE[cbRec]; XPtrST xRec((BYTE*)*ppvRec); GetVariableRecData( *ppvRec, cbRec ); xRec.Acquire(); return cbRec; } #endif //+--------------------------------------------------------------------------- // // Class: CRcovStrmReadIter // // Purpose: Read-only version of recoverable stream iterator. // // History: 1-25-94 srikants Created // //---------------------------------------------------------------------------- class CRcovStrmReadIter : public CRcovStrmIter { public: inline CRcovStrmReadIter( CRcovStrmReadTrans &readTrans, USHORT cbRec ); }; //+--------------------------------------------------------------------------- // // Method: CRcovStrmReadIter::CRcovStrmReadIter, public // // Synopsis: Constructor. Just squirrels away member variables. // // Arguments: [trans] -- Transaction context to iterate // [cbRec] -- Size of 'records' to be written. // // History: 27-May-1998 KyleP Added header // //---------------------------------------------------------------------------- inline CRcovStrmReadIter::CRcovStrmReadIter( CRcovStrmReadTrans & readTrans , USHORT cbRec ) : CRcovStrmIter( readTrans, cbRec ) { _trans.Seek(0); } //+--------------------------------------------------------------------------- // // Class: CRcovStrmAppendIter // // Purpose: Iterator to append fixed size records to the recoverable // streams. This is just an optimized case of a write iterator. // // History: 2-10-94 srikants Created // //---------------------------------------------------------------------------- class CRcovStrmAppendIter : public CRcovStrmIter { public: inline CRcovStrmAppendIter( CRcovStrmAppendTrans &appendTrans, USHORT cbRec ); inline void AppendRec( const void *pvRec ); inline void AppendVariableRec( const void *pvRec, ULONG cbRec ); private: CRcovStrmAppendTrans & _appendTrans; }; //+--------------------------------------------------------------------------- // // Method: CRcovStrmAppendIter::CRcovStrmAppendIter, public // // Synopsis: Constructor. Just squirrels away member variables. // // Arguments: [trans] -- Transaction context to iterate // [cbRec] -- Size of 'records' to be written. // // History: 27-May-1998 KyleP Added header // //---------------------------------------------------------------------------- inline CRcovStrmAppendIter::CRcovStrmAppendIter( CRcovStrmAppendTrans & trans, USHORT cbRec ) : CRcovStrmIter( trans, cbRec ), _appendTrans( trans ) { _appendTrans.Seek( ENDOFSTRM ); } //+--------------------------------------------------------------------------- // // Method: CRcovStrmAppendIter::AppendRec, public // // Synopsis: Appends record to end of stream. // // Arguments: [pvRec] -- Record to append. // // History: 27-May-1998 KyleP Added header // //---------------------------------------------------------------------------- inline void CRcovStrmAppendIter::AppendRec( const void *pvRec ) { _appendTrans.Append( pvRec, _cbRec ); ULONG ulCheckSum = CheckSum( pvRec ); _appendTrans.Append( &ulCheckSum, sizeof(ulCheckSum), FALSE ); } //+--------------------------------------------------------------------------- // // Method: CRcovStrmAppendIter::AppendVariableRec, public // // Synopsis: Appends variable size record to end of stream // // Arguments: [pvRec] -- Record to append. // [cbRec] -- Count in bytes of pvRec // // Returns: void // // History: 01-Jun-1998 VikasMan Created // //---------------------------------------------------------------------------- inline void CRcovStrmAppendIter::AppendVariableRec( const void *pvRec, ULONG cbRec ) { ULONG ulCheckSum; Win4Assert( cbRec != 0 && pvRec != 0 ); // first write the size _appendTrans.Append( &cbRec, sizeof( cbRec ), FALSE ); // next write the data _appendTrans.Append( pvRec, cbRec ); ulCheckSum = CheckSum( pvRec, cbRec ); _appendTrans.Append( &ulCheckSum, sizeof(ulCheckSum), FALSE ); } //+--------------------------------------------------------------------------- // // Class: CRcovStrmWriteIter // // Purpose: Iterator for writing fixed size anywhere in the // recoverable stream. // // History: 2-10-94 srikants Created // //---------------------------------------------------------------------------- class CRcovStrmWriteIter : public CRcovStrmIter { public: inline CRcovStrmWriteIter( CRcovStrmWriteTrans & writeTrans, USHORT cbRec ); inline BOOL SetRec( const void *pvBuf, ULONG iRec ); inline void SetRec( const void *pvBuf ); inline void AppendRec( const void *pvBuf ); #if 0 // untested unused new function inline void SetVariableRec( const void *pvRec, ULONG cbRec, BOOL fAppend = FALSE ); #endif private: CRcovStrmWriteTrans & _writeTrans; }; //+--------------------------------------------------------------------------- // // Method: CRcovStrmWriteIter::CRcovStrmWriteIter, public // // Synopsis: Constructor. Just squirrels away member variables. // // Arguments: [trans] -- Transaction context to iterate // [cbRec] -- Size of 'records' to be written. // // History: 27-May-1998 KyleP Added header // //---------------------------------------------------------------------------- inline CRcovStrmWriteIter::CRcovStrmWriteIter( CRcovStrmWriteTrans & writeTrans, USHORT cbRec ) : CRcovStrmIter( writeTrans, cbRec ), _writeTrans(writeTrans) { } //+--------------------------------------------------------------------------- // // Method: CRcovStrmWriteIter::SetRec, public // // Synopsis: Writes specified record to stream. // // Arguments: [pvRec] -- Record data // [iRec] -- Location at which write should occur // // History: 27-May-1998 KyleP Added header // //---------------------------------------------------------------------------- inline BOOL CRcovStrmWriteIter::SetRec( const void * pvRec, ULONG iRec ) { if (! _writeTrans.Seek( (_cbRec + _CHECKSUM_SIZE) * iRec ) || AtEnd() ) { return FALSE; } SetRec( pvRec ); return TRUE; } //+--------------------------------------------------------------------------- // // Method: CRcovStrmWriteIter::SetRec, public // // Synopsis: Writes specified record to current location in stream. // // Arguments: [pvRec] -- Record data // // History: 27-May-1998 KyleP Added header // //---------------------------------------------------------------------------- inline void CRcovStrmWriteIter::SetRec( const void * pvRec ) { _writeTrans.Write( pvRec, _cbRec ); ULONG ulCheckSum = CheckSum( pvRec ); _writeTrans.Write( &ulCheckSum, sizeof(ulCheckSum) ); } //+--------------------------------------------------------------------------- // // Method: CRcovStrmWriteIter::AppendRec, public // // Synopsis: Appends record to end of stream. // // Arguments: [pvRec] -- Record to append. // // History: 27-May-1998 KyleP Added header // //---------------------------------------------------------------------------- inline void CRcovStrmWriteIter::AppendRec( const void *pvRec ) { _writeTrans.Append( pvRec, _cbRec ); ULONG ulCheckSum = CheckSum( pvRec ); _writeTrans.Write( &ulCheckSum, sizeof(ulCheckSum) ); } #if 0 // untested unused new function //+--------------------------------------------------------------------------- // // Method: CRcovStrmWriteIter::SetVariableRec, public // // Synopsis: Writes/Appends variable size record to end of stream // // Arguments: [pvRec] -- Record to append. // [cbRec] -- Count in bytes of pvRec // [fAppend] -- If TRUE, then record is appended // // Returns: void // // History: 01-Jun-1998 VikasMan Created // //---------------------------------------------------------------------------- inline void CRcovStrmWriteIter::SetVariableRec( const void *pvRec, ULONG cbRec, BOOL fAppend /* = FALSE */ ) { ULONG ulCheckSum; Win4Assert( cbRec != 0 && pvRec != 0 ); // first write the size if ( fAppend ) _writeTrans.Append( &cbRec, sizeof( cbRec ) ); else _writeTrans.Write( &cbRec, sizeof( cbRec ) ); // next write the data _writeTrans.Write( pvRec, cbRec ); ulCheckSum = CheckSum( pvRec, cbRec ); _writeTrans.Write( &ulCheckSum, sizeof(ulCheckSum) ); } #endif