⛏️ index : haiku.git

/*
 * Copyright 2016 Dario Casalinuovo. All rights reserved.
 * Distributed under the terms of the MIT License.
 *
 */

#include "AdapterIO.h"

#include <MediaIO.h>

#include <string.h>

#include "MediaDebug.h"


#define TIMEOUT_QUANTA 100000


class RelativePositionIO : public BPositionIO {
public:
	RelativePositionIO(BAdapterIO* owner, BPositionIO* buffer,
		bigtime_t timeout)
		:
		BPositionIO(),
		fOwner(owner),
		fBackPosition(0),
		fStartOffset(0),
		fBuffer(buffer),
		fTimeout(timeout)
	{
	}

	virtual	~RelativePositionIO()
	{
		delete fBuffer;
	}

	status_t ResetStartOffset(off_t offset)
	{
		status_t ret = fBuffer->SetSize(0);
		if (ret != B_OK)
			return ret;

		fBackPosition = 0;
		fStartOffset = offset;
		return B_OK;
	}

	status_t FlushBefore(off_t position, BPositionIO* buffer, const void* oldBuffer,
		size_t oldLength)
	{
		AutoWriteLocker _(fLock);
		off_t relative = _PositionToRelative(position);
		if (relative < 0)
			return B_OK;
		if (relative > (off_t)oldLength)
			return B_BAD_VALUE;
		status_t status = buffer->WriteAt(0, (void*)((addr_t)oldBuffer + relative),
			oldLength - relative);
		if (status < B_OK)
			return status;
		status = buffer->Seek(fBuffer->Position() - relative, SEEK_SET);
		if (status < B_OK)
			return status;
		fBackPosition -= relative;
		fStartOffset += relative;
		SetBuffer(buffer);
		return B_OK;
	}

	status_t EvaluatePosition(off_t position, off_t totalSize)
	{
		if (position < 0)
			return B_ERROR;

		if (position < fStartOffset)
			return B_RESOURCE_UNAVAILABLE;

		if (totalSize > 0 && position > totalSize) {
			// This is an endless stream, we don't know
			// how much data will come and when, we could
			// block on that.
			if (IsMutable())
				return B_WOULD_BLOCK;
			else
				return B_ERROR;
		}

		return B_OK;
	}

	status_t WaitForData(off_t position, off_t size)
	{
		off_t bufferSize = 0;
		status_t ret = GetSize(&bufferSize);
		if (ret != B_OK)
			return B_ERROR;

		bigtime_t totalTimeOut = 0;

		while (bufferSize < position + size) {
			// We are not running, no luck to receive
			// more data, let's return and avoid locking.
			if (!fOwner->IsRunning())
				return B_NOT_SUPPORTED;

			if (fTimeout != B_INFINITE_TIMEOUT && totalTimeOut >= fTimeout)
				return B_TIMED_OUT;

			snooze(TIMEOUT_QUANTA);

			totalTimeOut += TIMEOUT_QUANTA;
			GetSize(&bufferSize);
		}
		return B_OK;
	}

	virtual	ssize_t	ReadAt(off_t position, void* buffer,
		size_t size)
	{
		AutoReadLocker _(fLock);

		return fBuffer->ReadAt(
			_PositionToRelative(position), buffer, size);

	}

	virtual	ssize_t	WriteAt(off_t position,
		const void* buffer, size_t size)
	{
		AutoWriteLocker _(fLock);

		return fBuffer->WriteAt(
			_PositionToRelative(position), buffer, size);
	}

	virtual	off_t Seek(off_t position, uint32 seekMode)
	{
		AutoWriteLocker _(fLock);

		if (seekMode == SEEK_SET)
			return fBuffer->Seek(_PositionToRelative(position), seekMode);
		return fBuffer->Seek(position, seekMode);
	}

	virtual off_t Position() const
	{
		AutoReadLocker _(fLock);

		return _RelativeToPosition(fBuffer->Position());
	}

	virtual	status_t SetSize(off_t size)
	{
		AutoWriteLocker _(fLock);

		return fBuffer->SetSize(_PositionToRelative(size));
	}

	virtual	status_t GetSize(off_t* size) const
	{
		AutoReadLocker _(fLock);

		// We use the backend position to make our buffer
		// independant of that.
		*size = _RelativeToPosition(fBackPosition);

		return B_OK;
	}

	ssize_t BackWrite(const void* buffer, size_t size)
	{
		AutoWriteLocker _(fLock);

		off_t ret = fBuffer->WriteAt(fBackPosition, buffer, size);
		fBackPosition += ret;
		return ret;
	}

	void SetBuffer(BPositionIO* buffer)
	{
		delete fBuffer;
		fBuffer = buffer;
	}

	bool IsStreaming() const
	{
		int32 flags = 0;
		fOwner->GetFlags(&flags);
		return ((flags & B_MEDIA_STREAMING) == B_MEDIA_STREAMING);
	}

	bool IsMutable() const
	{
		int32 flags = 0;
		fOwner->GetFlags(&flags);
		return ((flags & B_MEDIA_MUTABLE_SIZE) == B_MEDIA_MUTABLE_SIZE);
	}

	bool IsSeekable() const
	{
		int32 flags = 0;
		fOwner->GetFlags(&flags);
		return ((flags & B_MEDIA_SEEKABLE) == B_MEDIA_SEEKABLE);
	}

	const BPositionIO* Buffer() const
	{
		return fBuffer;
	}

private:

	off_t _PositionToRelative(off_t position) const
	{
		return position - fStartOffset;
	}

	off_t _RelativeToPosition(off_t position) const
	{
		return position + fStartOffset;
	}

	BAdapterIO*			fOwner;
	off_t				fBackPosition;
	off_t				fStartOffset;

	BPositionIO*		fBuffer;

	mutable	RWLocker	fLock;

	bigtime_t			fTimeout;
};


BAdapterIO::BAdapterIO(int32 flags, bigtime_t timeout)
	:
	fFlags(flags),
	fBuffer(NULL),
	fTotalSize(0),
	fOpened(false),
	fSeekSem(-1),
	fInputAdapter(NULL)
{
	CALLED();

	fBuffer = new RelativePositionIO(this, new BMallocIO(), timeout);
}


BAdapterIO::BAdapterIO(const BAdapterIO &)
{
	// copying not allowed...
}


BAdapterIO::~BAdapterIO()
{
	CALLED();

	delete fInputAdapter;
	delete fBuffer;
}


void
BAdapterIO::GetFlags(int32* flags) const
{
	CALLED();

	*flags = fFlags;
}


ssize_t
BAdapterIO::ReadAt(off_t position, void* buffer, size_t size)
{
	CALLED();

	status_t ret = _EvaluateWait(position, size);
	if (ret != B_OK)
		return ret;

	return fBuffer->ReadAt(position, buffer, size);
}


ssize_t
BAdapterIO::WriteAt(off_t position, const void* buffer, size_t size)
{
	CALLED();

	return fBuffer->WriteAt(position, buffer, size);
}


off_t
BAdapterIO::Seek(off_t position, uint32 seekMode)
{
	CALLED();

	off_t absolutePosition = 0;
	off_t size = 0;

	if (seekMode == SEEK_CUR)
		absolutePosition = Position()+position;
	else if (seekMode == SEEK_END) {
		if (GetSize(&size) != B_OK)
			return B_NOT_SUPPORTED;

		absolutePosition = size-position;
	}

	status_t ret = _EvaluateWait(absolutePosition, 0);

	if (ret == B_RESOURCE_UNAVAILABLE && fBuffer->IsStreaming()
			&& fBuffer->IsSeekable()) {

		fSeekSem = create_sem(0, "BAdapterIO seek sem");

		if (SeekRequested(absolutePosition) != B_OK)
			return B_NOT_SUPPORTED;

		TRACE("BAdapterIO::Seek: Locking on backend seek\n");
		acquire_sem(fSeekSem);
		TRACE("BAdapterIO::Seek: Seek completed!\n");
		fBuffer->ResetStartOffset(absolutePosition);
	} else if (ret != B_OK)
		return B_NOT_SUPPORTED;

	return fBuffer->Seek(position, seekMode);
}


off_t
BAdapterIO::Position() const
{
	CALLED();

	return fBuffer->Position();
}


status_t
BAdapterIO::SetSize(off_t size)
{
	CALLED();

	if (!fBuffer->IsMutable()) {
		fTotalSize = size;
		return B_OK;
	}

	return fBuffer->SetSize(size);
}


status_t
BAdapterIO::GetSize(off_t* size) const
{
	CALLED();

	if (!fBuffer->IsMutable()) {
		*size = fTotalSize;
		return B_OK;
	}

	return fBuffer->GetSize(size);
}


status_t
BAdapterIO::Open()
{
	CALLED();

	fOpened = true;
	return B_OK;
}


bool
BAdapterIO::IsRunning() const
{
	return fOpened;
}


void
BAdapterIO::SeekCompleted()
{
	CALLED();
	release_sem(fSeekSem);
	delete_sem(fSeekSem);
	fSeekSem = -1;
}


status_t
BAdapterIO::SetBuffer(BPositionIO* buffer)
{
	// We can't change the buffer while we
	// are running.
	if (fOpened)
		return B_ERROR;

	fBuffer->SetBuffer(buffer);
	return B_OK;
}


status_t
BAdapterIO::FlushBefore(off_t position)
{
	BMallocIO* buffer = new BMallocIO();
	BMallocIO* oldBuffer = (BMallocIO*)fBuffer->Buffer();
	fBuffer->FlushBefore(position, buffer, oldBuffer->Buffer(), oldBuffer->BufferLength());
	return B_OK;
}


BInputAdapter*
BAdapterIO::BuildInputAdapter()
{
	if (fInputAdapter != NULL)
		return fInputAdapter;

	fInputAdapter = new BInputAdapter(this);
	return fInputAdapter;
}


status_t
BAdapterIO::SeekRequested(off_t position)
{
	CALLED();

	return B_ERROR;
}


ssize_t
BAdapterIO::BackWrite(const void* buffer, size_t size)
{
	return fBuffer->BackWrite(buffer, size);
}


status_t
BAdapterIO::_EvaluateWait(off_t pos, off_t size)
{
	CALLED();

	off_t totalSize = 0;
	if (GetSize(&totalSize) != B_OK)
		TRACE("BAdapterIO::ReadAt: Can't get our size!\n");

	TRACE("BAdapterIO::_EvaluateWait TS %" B_PRId64 " P %" B_PRId64
		" S %" B_PRId64 "\n", totalSize, pos, size);

	status_t err = fBuffer->EvaluatePosition(pos, totalSize);

	TRACE("BAdapterIO::_EvaluateWait: %s\n", strerror(err));

	if (err != B_OK && err != B_WOULD_BLOCK)
		return err;

	TRACE("BAdapterIO::_EvaluateWait: waiting for data\n");

	return fBuffer->WaitForData(pos, size);
}


BInputAdapter::BInputAdapter(BAdapterIO* io)
	:
	fIO(io)
{
}


BInputAdapter::~BInputAdapter()
{
}


ssize_t
BInputAdapter::Write(const void* buffer, size_t size)
{
	return fIO->BackWrite(buffer, size);
}


// FBC
void BAdapterIO::_ReservedAdapterIO1() {}
void BAdapterIO::_ReservedAdapterIO2() {}
void BAdapterIO::_ReservedAdapterIO3() {}
void BAdapterIO::_ReservedAdapterIO4() {}
void BAdapterIO::_ReservedAdapterIO5() {}

void BInputAdapter::_ReservedInputAdapter1() {}
void BInputAdapter::_ReservedInputAdapter2() {}