// distribution boxbackup-0.10 (svn version: 494)
//  
// Copyright (c) 2003 - 2006
//      Ben Summers and contributors.  All rights reserved.
//  
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// 1. Redistributions of source code must retain the above copyright
//    notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in the
//    documentation and/or other materials provided with the distribution.
// 3. All use of this software and associated advertising materials must 
//    display the following acknowledgment:
//        This product includes software developed by Ben Summers.
// 4. The names of the Authors may not be used to endorse or promote
//    products derived from this software without specific prior written
//    permission.
// 
// [Where legally impermissible the Authors do not disclaim liability for 
// direct physical injury or death caused solely by defects in the software 
// unless it is modified by a third party.]
// 
// THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT,
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//  
//  
//  
// --------------------------------------------------------------------------
//
// File
//		Name:    ReadGatherStream.cpp
//		Purpose: Build a stream (for reading only) out of a number of other streams.
//		Created: 10/12/03
//
// --------------------------------------------------------------------------

#include "Box.h"

#include "ReadGatherStream.h"
#include "CommonException.h"

#include "MemLeakFindOn.h"

// --------------------------------------------------------------------------
//
// Function
//		Name:    ReadGatherStream::ReadGatherStream(bool)
//		Purpose: Constructor. Args says whether or not all the component streams will be deleted when this
//				 object is deleted.
//		Created: 10/12/03
//
// --------------------------------------------------------------------------
ReadGatherStream::ReadGatherStream(bool DeleteComponentStreamsOnDestruction)
	: mDeleteComponentStreamsOnDestruction(DeleteComponentStreamsOnDestruction),
	  mCurrentPosition(0),
	  mTotalSize(0),
	  mCurrentBlock(0),
	  mPositionInCurrentBlock(0),
	  mSeekDoneForCurrent(false)
{
}


// --------------------------------------------------------------------------
//
// Function
//		Name:    ReadGatherStream::~ReadGatherStream()
//		Purpose: Destructor. Will delete all the stream objects, if required.
//		Created: 10/12/03
//
// --------------------------------------------------------------------------
ReadGatherStream::~ReadGatherStream()
{
	// Delete compoenent streams?
	if(mDeleteComponentStreamsOnDestruction)
	{
		for(unsigned int l = 0; l < mComponents.size(); ++l)
		{
			delete mComponents[l];
		}
	}
}


// --------------------------------------------------------------------------
//
// Function
//		Name:    ReadGatherStream::AddComponent(IOStream *)
//		Purpose: Add a component to this stream, returning the index of this component
//				 in the internal list. Use this with AddBlock()
//		Created: 10/12/03
//
// --------------------------------------------------------------------------
int ReadGatherStream::AddComponent(IOStream *pStream)
{
	ASSERT(pStream != 0);

	// Just add the component to the list, returning it's index.
	int index = mComponents.size();
	mComponents.push_back(pStream);
	return index;
}


// --------------------------------------------------------------------------
//
// Function
//		Name:    ReadGatherStream::AddBlock(int, pos_type, bool, pos_type)
//		Purpose: Add a block to the list of blocks being gathered into one stream.
//				 Length is length of block to read from this component, Seek == true
//				 if a seek is required, and if true, SeekTo is the position (absolute)
//				 in the stream to be seeked to when this block is required.
//		Created: 10/12/03
//
// --------------------------------------------------------------------------
void ReadGatherStream::AddBlock(int Component, pos_type Length, bool Seek, pos_type SeekTo)
{
	// Check block
	if(Component < 0 || Component >= (int)mComponents.size() || Length < 0 || SeekTo < 0)
	{
		THROW_EXCEPTION(CommonException, ReadGatherStreamAddingBadBlock);
	}
	
	// Add to list
	Block b;
	b.mLength = Length;
	b.mSeekTo = SeekTo;
	b.mComponent = Component;
	b.mSeek = Seek;
	
	mBlocks.push_back(b);
	
	// And update the total size
	mTotalSize += Length;
}


// --------------------------------------------------------------------------
//
// Function
//		Name:    ReadGatherStream::Read(void *, int, int)
//		Purpose: As interface.
//		Created: 10/12/03
//
// --------------------------------------------------------------------------
int ReadGatherStream::Read(void *pBuffer, int NBytes, int Timeout)
{
	int bytesToRead = NBytes;
	uint8_t *buffer = (uint8_t*)pBuffer;
	
	while(bytesToRead > 0)
	{
		// Done?
		if(mCurrentBlock >= mBlocks.size())
		{
			// Stop now, as have finished the last block
			return NBytes - bytesToRead;
		}
			
		// Seek?
		if(mPositionInCurrentBlock == 0 && mBlocks[mCurrentBlock].mSeek && !mSeekDoneForCurrent)
		{
			// Do seeks in this manner so that seeks are done regardless of whether the block
			// has length > 0, and it will only be done once, and at as late a stage as possible.
			
			mComponents[mBlocks[mCurrentBlock].mComponent]->Seek(mBlocks[mCurrentBlock].mSeekTo, IOStream::SeekType_Absolute);
		
			mSeekDoneForCurrent = true;
		}

		// Anything in the current block?
		if(mPositionInCurrentBlock < mBlocks[mCurrentBlock].mLength)
		{
			// Read!
			int s = mBlocks[mCurrentBlock].mLength - mPositionInCurrentBlock;
			if(s > bytesToRead) s = bytesToRead;
			
			int r = mComponents[mBlocks[mCurrentBlock].mComponent]->Read(buffer, s, Timeout);
			
			// update variables
			mPositionInCurrentBlock += r;
			buffer += r;
			bytesToRead -= r;
			mCurrentPosition += r;
			
			if(r != s)
			{
				// Stream returned less than requested. To avoid blocking when not necessary,
				// return now.
				return NBytes - bytesToRead;
			}
		}
		else
		{
			// Move to next block
			++mCurrentBlock;
			mPositionInCurrentBlock = 0;
			mSeekDoneForCurrent = false;
		}
	}

	return NBytes - bytesToRead;
}


// --------------------------------------------------------------------------
//
// Function
//		Name:    ReadGatherStream::GetPosition()
//		Purpose: As interface
//		Created: 10/12/03
//
// --------------------------------------------------------------------------
IOStream::pos_type ReadGatherStream::GetPosition() const
{
	return mCurrentPosition;
}


// --------------------------------------------------------------------------
//
// Function
//		Name:    ReadGatherStream::BytesLeftToRead()
//		Purpose: As interface
//		Created: 10/12/03
//
// --------------------------------------------------------------------------
IOStream::pos_type ReadGatherStream::BytesLeftToRead()
{
	return mTotalSize - mCurrentPosition;
}


// --------------------------------------------------------------------------
//
// Function
//		Name:    ReadGatherStream::Write(const void *, int)
//		Purpose: As interface.
//		Created: 10/12/03
//
// --------------------------------------------------------------------------
void ReadGatherStream::Write(const void *pBuffer, int NBytes)
{
	THROW_EXCEPTION(CommonException, CannotWriteToReadGatherStream);
}


// --------------------------------------------------------------------------
//
// Function
//		Name:    ReadGatherStream::StreamDataLeft()
//		Purpose: As interface.
//		Created: 10/12/03
//
// --------------------------------------------------------------------------
bool ReadGatherStream::StreamDataLeft()
{
	if(mCurrentBlock >= mBlocks.size())
	{
		// Done all the blocks
		return false;
	}
	
	if(mCurrentBlock == (mBlocks.size() - 1)
		&& mPositionInCurrentBlock >= mBlocks[mCurrentBlock].mLength)
	{
		// Are on the last block, and have got all the data from it.
		return false;
	}

	// Otherwise, there's more data to be read
	return true;
}


// --------------------------------------------------------------------------
//
// Function
//		Name:    ReadGatherStream::StreamClosed()
//		Purpose: As interface. But the stream is always closed.
//		Created: 10/12/03
//
// --------------------------------------------------------------------------
bool ReadGatherStream::StreamClosed()
{
	return true;
}




syntax highlighted by Code2HTML, v. 0.9.1