⛏️ index : haiku.git

/*
 * Copyright 2003-2008, Haiku, Inc. All rights reserved.
 * Distributed under the terms of the MIT License.
 * 
 * Authors :
 *		Michael Wilber
 *		JΓ©rΓ΄me Duval
 */

#include <stdio.h>
#include <string.h>
#include "StreamBuffer.h"

#ifndef min
#define min(x,y) (((x) < (y)) ? (x) : (y))
#endif

#ifndef max
#define max(x,y) (((x) > (y)) ? (x) : (y))
#endif

// ---------------------------------------------------------------
// Constructor
//
// Initializes the StreamBuffer to read from pstream, buffering
// nbuffersize bytes of data at a time. Note that if nbuffersize
// is smaller than MIN_BUFFER_SIZE, MIN_BUFFER_SIZE is used
// as the buffer size.
//
// Preconditions:
//
// Parameters: pstream,	the stream to be buffered
//
//             nbuffersize,	number of bytes to be read from
//			                pstream at a time
//
// Postconditions:
//
// Returns:
// ---------------------------------------------------------------
StreamBuffer::StreamBuffer(BPositionIO *pstream, size_t nbuffersize, bool toRead)
{
	fStream = pstream;
	fBuffer = NULL;
	fBufferSize = 0;
	fLen = 0;
	fPos = 0;
	fToRead = toRead;
	
	if (!pstream)
		return;

	fBufferSize = max(nbuffersize, MIN_BUFFER_SIZE);
	fBuffer = new uint8[fBufferSize];
}

// ---------------------------------------------------------------
// Destructor
//
// Destroys data allocated for this object
//
// Preconditions:
//
// Parameters: 
//
// Postconditions:
//
// Returns:
// ---------------------------------------------------------------
StreamBuffer::~StreamBuffer()
{
	if (!fToRead && fLen > 0)
		fStream->Write(fBuffer, fLen);
	delete[] fBuffer;
	fBuffer = NULL;
}

// ---------------------------------------------------------------
// InitCheck
//
// Determines whether the constructor failed or not
//
// Preconditions: 
//
// Parameters:	
//
// Postconditions:
//
// Returns: B_OK if object has been initialized successfully,
// B_ERROR if not
// ---------------------------------------------------------------
status_t
StreamBuffer::InitCheck()
{
	if (fStream && fBuffer)
		return B_OK;
	else
		return B_ERROR;
}

// ---------------------------------------------------------------
// Read
//
// Copies up to nbytes of data from the stream into pinto
//
// Preconditions: ReadStream() must be called once before this
// function is called (the constructor does this)
//
// Parameters:	pinto,	the buffer to be copied to
//
//				nbytes,	the maximum number of bytes to copy
//
// Postconditions:
//
// Returns: the number of bytes successfully read or an
// error code returned by BPositionIO::Read()
// ---------------------------------------------------------------
ssize_t
StreamBuffer::Read(void *_pinto, size_t nbytes)
{
	if (_pinto == NULL)
		return B_BAD_VALUE;
	if (nbytes == 0)
		return 0;

	ssize_t result = B_ERROR;
	uint8 *pinto = (uint8 *)_pinto;
		
	size_t totalRead = min(nbytes, fLen - fPos);
	memcpy(pinto, fBuffer + fPos, totalRead);
	fPos += totalRead;
	pinto += totalRead;
	nbytes -= totalRead;
	
	while (nbytes > 0) {
		result = _ReadStream();
		if (result <= 0)
			return result;
		if (result > 0) {
			size_t left = min(nbytes, fLen - fPos);
			memcpy(pinto, fBuffer + fPos, left);
			fPos += left;
			pinto += left;
			nbytes -= left;
			totalRead += left;
		}
	}
	
	return totalRead;
}


// ---------------------------------------------------------------
// Write
//
// Copies up to nbytes of data from pinto into the stream
//
// Parameters:	pinto,	the buffer to be copied from
//				nbytes,	the maximum number of bytes to copy
//
// Returns: the number of bytes successfully read or an
// error code returned by BPositionIO::Read()
// ---------------------------------------------------------------
void
StreamBuffer::Write(void *pinto, size_t nbytes)
{
	if (nbytes < fBufferSize - fLen) {
		memcpy(fBuffer + fLen, pinto, nbytes);
		fLen += nbytes;
	} else {
		if (fLen > 0) {
			fStream->Write(fBuffer, fLen);
			fLen = 0;
		}
		fStream->Write(pinto, nbytes);
	}
}


// ---------------------------------------------------------------
// Seek
//
// Seeks the stream to the given position. If the seek operation fails, 
// the read buffer will be reset.
//
// Preconditions: fBuffer must be allocated and fBufferSize
// must be valid
//
// Parameters:	
//
// Postconditions:
//
// Returns: the new position
// ---------------------------------------------------------------
off_t
StreamBuffer::Seek(off_t position, uint32 seekMode)
{
	// just seek in the current buffer if the new position is in it
	if (seekMode == SEEK_CUR) {
		if (fToRead
			&& (fPos + position < fLen)
			&& (fPos + position >= 0)) {
			fPos += position;
			return Position();
		} else if (!fToRead
			&& (fLen + position < fBufferSize)
			&& (fLen + position >= 0)) {
			fLen += position;
			return Position();
		}
	} 
	
	// flush if something to write
	if (!fToRead
		&& fLen > 0) {
			fStream->Write(fBuffer, fLen);	
	}
	
	fLen = 0;
	fPos = 0;
		
	return fStream->Seek(position, seekMode);
}


// ---------------------------------------------------------------
// Position
//
// Returns the current position in the stream.
//
// Preconditions: fBuffer must be allocated and fBufferSize
// must be valid
//
// Parameters:	
//
// Postconditions:
//
// Returns: the position
// ---------------------------------------------------------------
off_t
StreamBuffer::Position()
{
	off_t position = fStream->Position();
	if (fToRead)
		position -= (fLen - fPos);
	else
		position += fLen;
	return position;
}


// ---------------------------------------------------------------
// _ReadStream
//
// Fills the stream buffer with data read in from the stream
//
// Preconditions: fBuffer must be allocated and fBufferSize
// must be valid
//
// Parameters:	
//
// Postconditions:
//
// Returns: the number of bytes successfully read or an
// error code returned by BPositionIO::Read()
// ---------------------------------------------------------------
ssize_t
StreamBuffer::_ReadStream()
{
	ssize_t len = fStream->Read(fBuffer, fBufferSize);
	if (len < 0)
		return len;
	fLen = len;
	fPos = 0;
	return fLen;
}