/*
 * Copyright 2014, Ingo Weinhold, ingo_weinhold@gmx.de.
 * Distributed under the terms of the MIT License.
 */
 
 
#include <CompressionAlgorithm.h>
 
#include <stdlib.h>
#include <string.h>
 
#include <Errors.h>
 
 
// #pragma mark - BCompressionParameters
 
 
BCompressionParameters::BCompressionParameters()
{
}
 
 
BCompressionParameters::~BCompressionParameters()
{
}
 
 
// #pragma mark - BDecompressionParameters
 
 
BDecompressionParameters::BDecompressionParameters()
{
}
 
 
BDecompressionParameters::~BDecompressionParameters()
{
}
 
 
// #pragma mark - BCompressionAlgorithm
 
 
BCompressionAlgorithm::BCompressionAlgorithm()
{
}
 
 
BCompressionAlgorithm::~BCompressionAlgorithm()
{
}
 
 
status_t
BCompressionAlgorithm::CreateCompressingInputStream(BDataIO* input,
	const BCompressionParameters* parameters, BDataIO*& _stream)
{
	return B_NOT_SUPPORTED;
}
 
 
status_t
BCompressionAlgorithm::CreateCompressingOutputStream(BDataIO* output,
	const BCompressionParameters* parameters, BDataIO*& _stream)
{
	return B_NOT_SUPPORTED;
}
 
 
status_t
BCompressionAlgorithm::CreateDecompressingInputStream(BDataIO* input,
	const BDecompressionParameters* parameters, BDataIO*& _stream)
{
	return B_NOT_SUPPORTED;
}
 
 
status_t
BCompressionAlgorithm::CreateDecompressingOutputStream(BDataIO* output,
	const BDecompressionParameters* parameters, BDataIO*& _stream)
{
	return B_NOT_SUPPORTED;
}
 
 
status_t
BCompressionAlgorithm::CompressBuffer(const void* input, size_t inputSize,
	void* output, size_t outputSize, size_t& _compressedSize,
	const BCompressionParameters* parameters)
{
	return B_NOT_SUPPORTED;
}
 
 
status_t
BCompressionAlgorithm::DecompressBuffer(const void* input,
	size_t inputSize, void* output, size_t outputSize,
	size_t& _uncompressedSize, const BDecompressionParameters* parameters)
{
	return B_NOT_SUPPORTED;
}
 
 
// #pragma mark - BAbstractStream
 
 
BCompressionAlgorithm::BAbstractStream::BAbstractStream()
	:
	BDataIO(),
	fBuffer(NULL),
	fBufferCapacity(0),
	fBufferOffset(0),
	fBufferSize(0)
{
}
 
 
BCompressionAlgorithm::BAbstractStream::~BAbstractStream()
{
	free(fBuffer);
}
 
 
status_t
BCompressionAlgorithm::BAbstractStream::Init(size_t bufferSize)
{
	fBuffer = (uint8*)malloc(bufferSize);
	fBufferCapacity = bufferSize;
 
	return fBuffer != NULL ? B_OK : B_NO_MEMORY;
}
 
 
// #pragma mark - BAbstractInputStream
 
 
BCompressionAlgorithm::BAbstractInputStream::BAbstractInputStream(
		BDataIO* input)
	:
	BAbstractStream(),
	fInput(input),
	fEndOfInput(false),
	fNoMorePendingData(false)
{
}
 
 
BCompressionAlgorithm::BAbstractInputStream::~BAbstractInputStream()
{
}
 
 
ssize_t
BCompressionAlgorithm::BAbstractInputStream::Read(void* buffer, size_t size)
{
	if (size == 0)
		return 0;
 
	size_t bytesRemaining = size;
	uint8* output = (uint8*)buffer;
 
	while (bytesRemaining > 0) {
		// process the data still in the input buffer
		if (fBufferSize > 0) {
			size_t bytesConsumed;
			size_t bytesProduced;
			status_t error = ProcessData(fBuffer + fBufferOffset, fBufferSize,
				output, bytesRemaining, bytesConsumed, bytesProduced);
			if (error != B_OK)
				return error;
 
			fBufferOffset += bytesConsumed;
			fBufferSize -= bytesConsumed;
			output += bytesProduced;
			bytesRemaining -= bytesProduced;
			continue;
		}
 
		// We couldn't process anything, because we don't have any or not enough
		// bytes in the input buffer.
 
		if (fEndOfInput)
			break;
 
		// Move any remaining data to the start of the buffer.
		if (fBufferSize > 0) {
			if (fBufferSize == fBufferCapacity)
				return B_ERROR;
 
			if (fBufferOffset > 0)
				memmove(fBuffer, fBuffer + fBufferOffset, fBufferSize);
		}
 
		fBufferOffset = 0;
 
		// read from the source
		ssize_t bytesRead = fInput->Read(fBuffer + fBufferSize,
			fBufferCapacity - fBufferSize);
		if (bytesRead < 0)
			return bytesRead;
		if (bytesRead == 0) {
			fEndOfInput = true;
			break;
		}
 
		fBufferSize += bytesRead;
	}
 
	// If we've reached the end of the input and still have room in the output
	// buffer, we have consumed all input data and want to flush all pending
	// data, now.
	if (fEndOfInput && bytesRemaining > 0 && !fNoMorePendingData) {
		size_t bytesProduced;
		status_t error = FlushPendingData(output, bytesRemaining,
			bytesProduced);
		if (error != B_OK)
			return error;
 
		if (bytesProduced < bytesRemaining)
			fNoMorePendingData = true;
 
		output += bytesProduced;
		bytesRemaining -= bytesProduced;
	}
 
	return size - bytesRemaining;
}
 
 
// #pragma mark - BAbstractOutputStream
 
 
BCompressionAlgorithm::BAbstractOutputStream::BAbstractOutputStream(
		BDataIO* output)
	:
	BAbstractStream(),
	fOutput(output)
{
}
 
 
BCompressionAlgorithm::BAbstractOutputStream::~BAbstractOutputStream()
{
}
 
 
ssize_t
BCompressionAlgorithm::BAbstractOutputStream::Write(const void* buffer,
	size_t size)
{
	if (size == 0)
		return 0;
 
	size_t bytesRemaining = size;
	uint8* input = (uint8*)buffer;
 
	while (bytesRemaining > 0) {
		// try to process more data
		if (fBufferSize < fBufferCapacity) {
			size_t bytesConsumed;
			size_t bytesProduced;
			status_t error = ProcessData(input, bytesRemaining,
				fBuffer + fBufferSize, fBufferCapacity - fBufferSize,
				bytesConsumed, bytesProduced);
			if (error != B_OK)
				return error;
 
			input += bytesConsumed;
			bytesRemaining -= bytesConsumed;
			fBufferSize += bytesProduced;
			continue;
		}
 
		// We couldn't process anything, because we don't have any or not enough
		// room in the output buffer.
 
		if (fBufferSize == 0)
			return B_ERROR;
 
		// write to the target
		ssize_t bytesWritten = fOutput->Write(fBuffer, fBufferSize);
		if (bytesWritten < 0)
			return bytesWritten;
		if (bytesWritten == 0)
			break;
 
		// Move any remaining data to the start of the buffer.
		fBufferSize -= bytesWritten;
		if (fBufferSize > 0)
			memmove(fBuffer, fBuffer + bytesWritten, fBufferSize);
	}
 
	return size - bytesRemaining;
}
 
 
status_t
BCompressionAlgorithm::BAbstractOutputStream::Flush()
{
	bool noMorePendingData = false;
 
	for (;;) {
		// let the derived class flush all pending data
		if (fBufferSize < fBufferCapacity && !noMorePendingData) {
			size_t bytesProduced;
			status_t error = FlushPendingData(fBuffer + fBufferSize,
				fBufferCapacity - fBufferSize, bytesProduced);
			if (error != B_OK)
				return error;
 
			noMorePendingData = bytesProduced < fBufferCapacity - fBufferSize;
 
			fBufferSize += bytesProduced;
		}
 
		// write buffered data to output
		if (fBufferSize == 0)
			break;
 
		status_t error = fOutput->WriteExactly(fBuffer, fBufferSize);
		if (error != B_OK)
			return error;
 
		fBufferSize = 0;
	}
 
	return fOutput->Flush();
}

V547 Expression 'fBufferSize > 0' is always false.