#pragma region CPL License
/*
Nuclex Native Framework
Copyright (C) 2002-2021 Nuclex Development Labs
This library is free software; you can redistribute it and/or
modify it under the terms of the IBM Common Public License as
published by the IBM Corporation; either version 1.0 of the
License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
IBM Common Public License for more details.
You should have received a copy of the IBM Common Public
License along with this library
*/
#pragma endregion // CPL License
#ifndef NUCLEX_STORAGE_COMPRESSION_CSC_CSCDECOMPRESSOR_H
#define NUCLEX_STORAGE_COMPRESSION_CSC_CSCDECOMPRESSOR_H
#include "Nuclex/Storage/Config.h"
#if defined(NUCLEX_STORAGE_HAVE_CSC)
#include "Nuclex/Storage/Compression/Decompressor.h"
#include "../../Helpers/ReadBuffer.h"
#include "CscHelper.h"
// Prototype for internal implementation from csc_dec.cpp
class CSCDecoder;
namespace Nuclex { namespace Storage { namespace Compression { namespace Csc {
// ------------------------------------------------------------------------------------------- //
///
/// Decompresses data that has been compressed by the CSC library and algorithm
///
class CscDecompressor : public Decompressor {
/// Initializes a new CSC decompressor
public: CscDecompressor();
/// Frees all resources owned by the instance
public: ~CscDecompressor() override;
///
/// Decompresses the data in the input buffer and writes it to the output buffer
///
/// Buffer containing the compressed data
///
/// Number of compressed bytes in the compressed buffer. Will be set to
/// the number of remaining bytes when the method returns
///
/// Buffer in which the uncompressed data will be stored
///
/// Available space in the output buffer. Will be set to the bytes actually stored
/// in the output buffer when the method returns
///
///
/// The reason why the method stopped processing data. This may either be because
/// all available input was decompressed, or because the decompressor ran out of
/// space in the output buffer.
///
public: StopReason Process(
const std::uint8_t *compressedBuffer, std::size_t &compressedByteCount,
std::uint8_t *outputBuffer, std::size_t &outputByteCount
) override;
/// Finishes decompressing and writes any remaining output bytes
/// Buffer in which the decompressed data will be stored
///
/// Available space in the output buffer. Will be set to the bytes actually stored
/// in the output buffer when the method returns.
///
///
/// The reason why the method stopped processing. This should normally be the
/// value but may also be
/// if the output buffer was insufficient
/// to output all data (in which case you need to call Finish() another time).
///
public: StopReason Finish(
std::uint8_t *outputBuffer, std::size_t &outputByteCount
) override;
/// Initializes the CSC properties, decoder and output buffer
private: void initializeCscDecoder();
/// Initializes the CSC properties, decoder and output buffer
/// Buffer into which pending output is copied
///
/// Maximum number of bytes to place in the output buffer
///
/// The number of bytes actually placed in the output buffer
private: std::size_t extractPendingOutput(
std::uint8_t *outputBuffer, std::size_t outputByteCount
);
#pragma region class SequentialInputStream
private: class SequentialInputStream : public ISeqInStream {
/// Initializes a new buffered output stream
///
/// Split buffer writer into which all written data will be directed
///
public: SequentialInputStream(Nuclex::Storage::Helpers::ReadBuffer &buffer) :
buffer(buffer) {
this->Read = &read;
}
/// Method that is registered to libcsc as the write function pointer
/// Pointer to the output stream
/// Buffer holding the data that will be written
/// Number of bytes that will be written
/// The number of bytes actually written
private: static SRes read(void *self, void *buffer, size_t *byteCount) {
SequentialInputStream &selfAsStream = *reinterpret_cast(self);
std::size_t availableByteCount = selfAsStream.buffer.CountAvailableBytes();
if(availableByteCount < *byteCount) {
*byteCount = availableByteCount;
}
selfAsStream.buffer.Read(reinterpret_cast(buffer), *byteCount);
return SZ_OK;
}
/// Buffer from which all input data is taken
private: Nuclex::Storage::Helpers::ReadBuffer &buffer;
};
#pragma endregion // class SequentialInputStream
/// Allocator that allows libcsc to reuse memory
private: ReusingAllocator allocator;
/// Properties the CSC encoder has been configured with
private: ::CSCProps encoderProperties; // uninitialized if (decoderHandle == nullptr)
/// Whether the CSC decoder has been initialized
private: bool decoderInitialized;
/// Maintains the CSC decoder's state
private: ::CSCDecHandle decoderHandle;
/// The CSC decoder instance fished out of the decoder handle
private: ::CSCDecoder *decoder;
/// Receives the decoder properties
private: std::uint8_t decoderPropertyMemory[10];
/// Number of bytes currently copied into the decoder property memory
private: std::size_t decoderPropertyMemoryByteCount;
/// Buffers input for when we need to accumulate enough bytes
private: Nuclex::Storage::Helpers::ReadBuffer inputBuffer;
/// Input stream from which data is fed to the CSC decompressor
private: SequentialInputStream inputStream;
/// Separate output buffer because we have no control over extraction
private: std::uint8_t *outputBuffer;
/// Index at which the next read takes data from the output buffer
private: std::size_t outputBufferStartIndex;
/// Index one past the last byte currently stored in the output buffer
private: std::size_t outputBufferEndIndex;
};
// ------------------------------------------------------------------------------------------- //
}}}} // namespace Nuclex::Storage::Compression::Csc
#endif // defined(NUCLEX_STORAGE_HAVE_CSC)
#endif // NUCLEX_STORAGE_COMPRESSION_CSC_CSCDECOMPRESSOR_H