root/src/kits/support/CompressionAlgorithm.cpp
/*
 * 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 iovec& input, iovec& output,
        const BCompressionParameters* parameters, iovec* scratch)
{
        return B_NOT_SUPPORTED;
}


status_t
BCompressionAlgorithm::DecompressBuffer(const iovec& input, iovec& output,
        const BDecompressionParameters* parameters, iovec* scratch)
{
        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();
}