⛏️ index : haiku.git

/*
 * Copyright 2011-2013, Axel DΓΆrfler, axeld@pinc-software.de.
 * Distributed under the terms of the MIT License.
 */


#include <BufferedDataIO.h>

#include <new>

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


//#define TRACE_DATA_IO
#ifdef TRACE_DATA_IO
#	define TRACE(x...) printf(x)
#else
#	define TRACE(x...) ;
#endif


BBufferedDataIO::BBufferedDataIO(BDataIO& stream, size_t bufferSize,
	bool ownsStream, bool partialReads)
	:
	fStream(stream),
	fPosition(0),
	fSize(0),
	fDirty(false),
	fOwnsStream(ownsStream),
	fPartialReads(partialReads)
{
	fBufferSize = max_c(bufferSize, 512);
	fBuffer = new(std::nothrow) uint8[fBufferSize];
}


BBufferedDataIO::~BBufferedDataIO()
{
	Flush();
	delete[] fBuffer;

	if (fOwnsStream)
		delete &fStream;
}


status_t
BBufferedDataIO::InitCheck() const
{
	return fBuffer == NULL ? B_NO_MEMORY : B_OK;
}


BDataIO*
BBufferedDataIO::Stream() const
{
	return &fStream;
}


size_t
BBufferedDataIO::BufferSize() const
{
	return fBufferSize;
}


bool
BBufferedDataIO::OwnsStream() const
{
	return fOwnsStream;
}


void
BBufferedDataIO::SetOwnsStream(bool ownsStream)
{
	fOwnsStream = ownsStream;
}


status_t
BBufferedDataIO::Flush()
{
	if (!fDirty)
		return B_OK;

	ssize_t bytesWritten = fStream.Write(fBuffer + fPosition, fSize);
	if ((size_t)bytesWritten == fSize) {
		fDirty = false;
		fPosition = 0;
		fSize = 0;
		return B_OK;
	} else if (bytesWritten >= 0) {
		fSize -= bytesWritten;
		fPosition += bytesWritten;
		return B_PARTIAL_WRITE;
	}

	return B_OK;
}


ssize_t
BBufferedDataIO::Read(void* buffer, size_t size)
{
	if (buffer == NULL)
		return B_BAD_VALUE;

	TRACE("%p::Read(size %lu)\n", this, size);

	size_t bytesRead = 0;

	if (fSize > 0) {
		// fill the part of the stream we already have
		bytesRead = min_c(size, fSize);
		TRACE("%p: read %lu bytes we already have in the buffer.\n", this,
			bytesRead);
		memcpy(buffer, fBuffer + fPosition, bytesRead);

		buffer = (void*)((uint8_t*)buffer + bytesRead);
		size -= bytesRead;
		fPosition += bytesRead;
		fSize -= bytesRead;

		if (fPartialReads)
			return bytesRead;
	}

	if (size > fBufferSize || fBuffer == NULL) {
		// request is larger than our buffer, just fill it directly
		return fStream.Read(buffer, size);
	}

	if (size > 0) {
		// retrieve next buffer

		status_t status = Flush();
		if (status != B_OK)
			return status;

		TRACE("%p: read %" B_PRIuSIZE " bytes from stream\n", this,
			fBufferSize);
		ssize_t nextRead = fStream.Read(fBuffer, fBufferSize);
		if (nextRead < 0)
			return nextRead;

		fSize = nextRead;
		TRACE("%p: retrieved %" B_PRIuSIZE " bytes from stream\n", this, fSize);
		fPosition = 0;

		// Copy the remaining part
		size_t copy = min_c(size, fSize);
		memcpy(buffer, fBuffer, copy);
		TRACE("%p: copy %" B_PRIuSIZE" bytes to buffer\n", this, copy);

		bytesRead += copy;
		fPosition = copy;
		fSize -= copy;
	}

	return bytesRead;
}


ssize_t
BBufferedDataIO::Write(const void* buffer, size_t size)
{
	if (buffer == NULL)
		return B_BAD_VALUE;

	TRACE("%p::Write(size %lu)\n", this, size);

	if (size > fBufferSize || fBuffer == NULL) {
		// request is larger than our buffer, just fill it directly
		status_t status = Flush();
		if (status != B_OK)
			return status;

		return fStream.Write(buffer, size);
	}

	if (!fDirty) {
		// Throw away a read-only buffer if necessary
		TRACE("%p: throw away previous buffer.\n", this);
		fPosition = 0;
		fSize = 0;
	}

	size_t bytesWritten = 0;
	while (size > 0) {
		size_t toCopy = min_c(size, fBufferSize - (fPosition + fSize));
		TRACE("%p: write %" B_PRIuSIZE " bytes to the buffer.\n", this,
			toCopy);
		memcpy(fBuffer + (fPosition + fSize), buffer, toCopy);
		fSize += toCopy;
		bytesWritten += toCopy;
		size -= toCopy;
		fDirty = true;

		if ((fPosition + fSize) == fBufferSize) {
			status_t status = Flush();
			if (status != B_OK)
				return bytesWritten;
		}
	}

	return bytesWritten;
}


//	#pragma mark - FBC


status_t BBufferedDataIO::_Reserved0(void*) { return B_ERROR; }
status_t BBufferedDataIO::_Reserved1(void*) { return B_ERROR; }
status_t BBufferedDataIO::_Reserved2(void*) { return B_ERROR; }
status_t BBufferedDataIO::_Reserved3(void*) { return B_ERROR; }
status_t BBufferedDataIO::_Reserved4(void*) { return B_ERROR; }