Click here to Skip to main content
15,886,518 members
Articles / Programming Languages / C#

Managed C++ wrapper for ZLib

Rate me:
Please Sign up or sign in to vote.
4.56/5 (14 votes)
3 Mar 2005CPOL9 min read 155.2K   5.7K   46  
.NET wrapper for ZLib, written in MC++
/*
DevelopDotNet
http://www.developdotnet.com

File Created by: Alberto Ferrazzoli
Date: 20/05/2004

Class Description:
ZStream

Notes:

Revision Log - Please mark significant changes in source code in the following format:

Date  -  Time -  Reviewer  -			Comments
10/07/2004		 Alberto Ferrazzoli		ZStream header presence selection for compatibility with
										raw zip file format.
15/07/2004		 Alberto Ferrazzoli		Bug Fix on ZFlush() and ZFinish()
23/07/2004		 Alberto Ferrazzoli		Bug Fix ZLibDeflateCleanUp()
25/07/2004		 Alberto Ferrazzoli		Support for read only ZStream.
*/

#include "stdafx.h"
#include "RS.h"
#include "IChecksum.h"
#include "ZStream.h"
#using <mscorlib.dll>

using namespace DevelopDotNet::Compression;

ZStream::ZStream(Stream *stream)
{
	_canWrite = false;
	_canRead = true;

	Initialize(stream, CompressionLevel::Normal, CompressionStrategy::DefaultStrategy, MAX_WBITS);
}

ZStream::ZStream(Stream *stream, bool write)
{
	_canWrite = write;
	_canRead = !write;

	Initialize(stream, CompressionLevel::Normal, CompressionStrategy::DefaultStrategy, MAX_WBITS);
}

ZStream::ZStream(Stream *stream, CompressionLevel level)
{
	_canWrite = true;
	_canRead = false;

	Initialize(stream, level, CompressionStrategy::DefaultStrategy, MAX_WBITS);
}

ZStream::ZStream(Stream *stream, CompressionLevel level, CompressionStrategy strategy)
{
	_canWrite = true;
	_canRead = false;

	Initialize(stream, level, strategy, MAX_WBITS);
}

ZStream::ZStream(Stream *stream, bool write, int bits)
{
	_canWrite = write;
	_canRead = !write;

	Initialize(stream, CompressionLevel::Normal, CompressionStrategy::DefaultStrategy, bits);
}

ZStream::ZStream(Stream *stream, bool write, int bits, IChecksum* checksum)
{
	_canWrite = write;
	_canRead = !write;
	_checksum = checksum;

	Initialize(stream, CompressionLevel::Normal, CompressionStrategy::DefaultStrategy, bits);
}

ZStream::ZStream(Stream *stream, CompressionLevel level, CompressionStrategy strategy, int bits)
{
	_canWrite = true;
	_canRead = false;

	Initialize(stream, level, strategy, bits);
}

ZStream::ZStream(Stream *stream, CompressionLevel level, CompressionStrategy strategy, int bits, IChecksum* checksum)
{
	_canWrite = true;
	_canRead = false;
	_checksum = checksum;

	Initialize(stream, level, strategy, bits);
}

void ZStream::Initialize(Stream *stream, CompressionLevel level, CompressionStrategy strategy, int windowBits)
{
	_disposed = false;
	_ownerBaseStream = false;
	m_zbufLen = 0;
	m_zcurPos = 0;

	_canSeek = false;

	_stream = stream;
	m_zbuffer = __gc new unsigned char __gc[BUFFER_SIZE];

	if (_canRead)
	{
		if (! _stream->CanRead)
		{
			throw __gc new NotSupportedException(RS::GetResourceString(S"NoRead"));
		}
		ZLibInflateInit(windowBits);
	}
	else
	{
		if (! _stream->CanWrite)
		{
			throw __gc new NotSupportedException(RS::GetResourceString(S"NoWrite"));
		}
		if ( Z_OK == ZLibDeflateInit(windowBits))
		{
			ZLibParam(level, strategy);
		}
	}
}

int ZStream::ZLibInflateInit(int windowBits)
{
	z_stream __pin * zstmp = &m_zstm;
	int nRes = inflateInit2(zstmp, windowBits);
	if (Z_OK != nRes)
	{
		throw __gc new ZException(nRes, S"Inflate init error");
	}
	
	return nRes;
}

int ZStream::ZLibInflateCleanUp()
{
	z_stream __pin * zstmp = &m_zstm;
	int nRes = inflateEnd(zstmp);

	if (Z_OK != nRes)
	{
		throw __gc new ZException(nRes, S"Inflate cleanup error");
	}

	return nRes;
}

int ZStream::ZLibDeflateInit(int windowBits)
{
	z_stream __pin * zstmp = &m_zstm;
	int nRes = deflateInit2(zstmp, Z_DEFAULT_COMPRESSION, Z_DEFLATED, windowBits, MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY);

	if (Z_OK != nRes)
	{
		throw __gc new ZException(nRes, S"Deflate init error");
	}

	return nRes;
}

int ZStream::ZLibDeflateCleanUp()
{
	int nRes;

	BYTE __pin *zbuff = &m_zbuffer[0];

	if (m_zbufLen > 0)
	{
		_stream->Write(m_zbuffer, 0, m_zbufLen);
		m_zbufLen = 0;
	}

	z_stream __pin * zstmp = &m_zstm;
	
	do 
	{
		int nDone = m_zstm.total_out;
		m_zstm.next_out = (Bytef*) &zbuff[m_zbufLen];
		m_zstm.avail_out = (uInt) (BUFFER_SIZE - m_zbufLen);

		nRes = deflate(zstmp, Z_FINISH);

		if(nRes < 0)
			break;

		nDone = m_zstm.total_out - nDone;
		_stream->Write(m_zbuffer, 0, nDone);

	} while(Z_OK == nRes);

	if(Z_STREAM_END == nRes)
		nRes = deflateEnd(zstmp);

	if (Z_OK != nRes)
	{
		throw __gc new ZException(nRes, S"Deflate cleanup error");
	}

	return nRes;
}

bool ZStream::ZLibParam(CompressionLevel level, CompressionStrategy strategy)
{
	bool bRes = false;
	z_stream __pin * zstmp = &m_zstm;
	int nRes = deflateParams(zstmp, level, strategy);
	
	if (Z_OK == nRes)
	{
		_compressionlevel = level;
		_compressionstrategy = strategy;
		bRes = true;
	}
	return bRes;
}

__int64 ZStream::get_Length()
{
	return _stream->Length;
}

__int64 ZStream::get_Position()
{
	return _stream->Position;
}

void ZStream::set_Position(__int64 /* pos */)
{
	throw __gc new NotSupportedException(RS::GetResourceString(S"NoSeek"));
}

void ZStream::set_Level(CompressionLevel value)
{
	if (!_canWrite)
	{
		throw __gc new NotSupportedException(RS::GetResourceString(S"NoWrite"));
	}
	ZLibParam(value, _compressionstrategy);
}

void ZStream::set_Strategy(CompressionStrategy value)
{
	if (!_canWrite)
	{
		throw __gc new NotSupportedException(RS::GetResourceString(S"NoWrite"));
	}
	ZLibParam(_compressionlevel, value);
}

__int64 ZStream::get_CompressedLength()
{
	return _canWrite ? m_zstm.total_out : m_zstm.total_in;
}

__int64 ZStream::get_UncompressedLength()
{
	return _canWrite ? m_zstm.total_in : m_zstm.total_out;
}

CompressedDataType ZStream::get_DataType()
{
	CompressedDataType datatype = CompressedDataType::Unknown;
	
	switch(m_zstm.data_type)
	{
	case Z_BINARY:
		datatype = CompressedDataType::Binary;
		break;
	case Z_ASCII:
		datatype = CompressedDataType::Ascii;
		break;
	case Z_UNKNOWN:
	default:
		break;
	}

	return datatype;
}

String* ZStream::get_ZLibVersion()
{ 
	const char * version = zlibVersion();
	return __gc new String(version);
}

int ZStream::get_Checksum()
{
	int checksum = _checksum != NULL ? _checksum->Checksum : m_zstm.adler;
	return checksum;
}

int ZStream::Read([In, Out] unsigned char buffer __gc[], int offset, int count)
{
	if (NULL == buffer)
		throw __gc new ArgumentNullException(S"buffer", RS::GetResourceString(S"BufferNotNull"));

	if (offset < 0)
		throw __gc new ArgumentOutOfRangeException(S"offset", __box(offset),
			String::Concat(S"offset ", RS::GetResourceString(S"ParamNegative"))
		);

	if (count < 0)
		throw __gc new ArgumentOutOfRangeException(S"count", __box(count),
			String::Concat(S"count ", RS::GetResourceString(S"ParamNegative"))
		);

	if (buffer->Length - offset < count)
		throw __gc new ArgumentException(RS::GetResourceString(S"CountExceed"));

	if (!_canRead)
	{
		throw __gc new NotSupportedException(RS::GetResourceString(S"NoRead"));
	}

	int nRead = 0;
	int nRes = Z_ERRNO;

	try
	{
		nRes = ZRead(&buffer[offset], count, &nRead);
		
		if(NULL != _checksum)
		{
			_checksum->Update(buffer, offset, count);
		}
	}
	catch(IOException* e)
	{
		throw __gc new ZException(nRes, S"Inflate error", e);
	}
	
	if(nRes < 0)
	{
		throw __gc new ZException(nRes, S"Inflate error");
	}

	return nRead;
}

int ZStream::ZRead(unsigned char __gc * buffer, int cb, int *pcbRead)
{
	BYTE __pin *zbuff = &m_zbuffer[0];

	unsigned char __pin * pv = buffer;
	m_zstm.next_out = pv;
	m_zstm.avail_out = (uInt) cb;

	if (m_zcurPos == m_zbufLen)
	{
		m_zbufLen = _stream->Read(m_zbuffer, 0, BUFFER_SIZE);
		m_zcurPos = 0;
	}

	int nRes = Z_OK;
	do
	{
		m_zstm.next_in = (Bytef *) &zbuff[m_zcurPos];
		m_zstm.avail_in = (uInt) (m_zbufLen - m_zcurPos);

		if (m_zstm.avail_in == 0)
		{
			break;
		}

		z_stream __pin * zstmp = &m_zstm;
		nRes = inflate(zstmp, Z_SYNC_FLUSH);
		if (nRes != Z_OK)
			break;

		if (m_zstm.avail_out == 0)
		{
			m_zcurPos = m_zstm.next_in - (Bytef*) &zbuff[0];
			break;
		}

		m_zbufLen = _stream->Read(m_zbuffer, 0, BUFFER_SIZE);
		m_zcurPos = 0;
	} while (m_zstm.avail_out > 0);

	if (pcbRead)
		*pcbRead = cb - m_zstm.avail_out;

	return nRes;
}

void ZStream::Write(unsigned char buffer __gc[], int offset, int count)
{
	if (NULL == buffer)
		throw new ArgumentNullException(S"buffer", RS::GetResourceString(S"BufferNotNull"));

	if (offset < 0)
		throw __gc new ArgumentOutOfRangeException(S"offset", __box(offset),
			String::Concat(S"offset ", RS::GetResourceString(S"ParamNegative"))
		);

	if (count < 0)
		throw __gc new ArgumentOutOfRangeException(S"count", __box(count),
			String::Concat(S"count ", RS::GetResourceString(S"ParamNegative"))
		);

	if (buffer->Length - offset < count)
		throw new ArgumentException(RS::GetResourceString(S"CountExceed"));

	if (!_canWrite)
	{
		throw __gc new NotSupportedException(RS::GetResourceString(S"NoWrite"));
	}

	int nRes = Z_ERRNO;

	try
	{
		if(NULL != _checksum)
		{
			_checksum->Update(buffer, offset, count);
		}

		nRes = ZWrite(&buffer[offset], count);
	}
	catch(IOException* e)
	{
		throw __gc new ZException(nRes, S"Deflate error", e);
	}

	if(nRes < 0)
	{
		throw __gc new ZException(nRes, S"Deflate error");
	}
}

int ZStream::ZWrite(unsigned char __gc * buffer, int cb)
{
	BYTE __pin *zbuff = &m_zbuffer[0];

	// Prepare input buffer
	unsigned char __pin * pv = buffer;
	m_zstm.next_in = pv;
	m_zstm.avail_in = (uInt) cb;

	if(BUFFER_SIZE == m_zbufLen)
	{
		_stream->Write(m_zbuffer, 0, m_zbufLen);
		m_zbufLen = 0;
	}

	int nRes = Z_OK;
	do
	{
		int nDone = m_zstm.total_out;
		m_zstm.next_out = (Bytef*) &zbuff[m_zbufLen];
		m_zstm.avail_out = (uInt) (BUFFER_SIZE - m_zbufLen);

		z_stream __pin * zstmp = &m_zstm;
		nRes = deflate(zstmp, Z_NO_FLUSH);
		if (nRes != Z_OK)
			break;

		nDone = m_zstm.total_out - nDone;
		if(m_zstm.avail_in == 0)
		{
			m_zbufLen += nDone;
			break;
		}

		_stream->Write(m_zbuffer, 0, nDone);
		m_zbufLen = 0;
	} while (m_zstm.avail_in > 0);

	return nRes;
}

__int64 ZStream::Seek(__int64 offset, System::IO::SeekOrigin origin)
{
	if(_canRead)
	{
		return _stream->Seek(offset, origin);
	}
	
	throw new NotSupportedException(RS::GetResourceString(S"NoSeek"));
}

void ZStream::Flush()
{
	if(_canWrite)
	{
		int nRes = ZFlush();
		if(Z_OK != nRes)
		{
			throw __gc new ZException(nRes, S"Flush error");
		}
	}
	_stream->Flush();
}

int ZStream::ZFlush()
{
	int nRes = Z_OK;

	BYTE __pin *zbuff = &m_zbuffer[0];

	if (m_zbufLen > 0)
	{
		_stream->Write(m_zbuffer, 0, m_zbufLen);
		m_zbufLen = 0;
	}

	do
	{
		int nDoneUntillNow = m_zstm.total_out;
		m_zstm.next_out = (Bytef*) &zbuff[m_zbufLen];
		m_zstm.avail_out = (uInt) (BUFFER_SIZE - m_zbufLen);

		z_stream __pin * zstmp = &m_zstm;
		nRes = deflate(zstmp, Z_FULL_FLUSH);
		if (nRes != Z_OK)
			break;

		m_zbufLen += m_zstm.total_out - nDoneUntillNow;

		_stream->Write(m_zbuffer, 0, m_zbufLen);
		m_zbufLen = 0;
	} while(m_zstm.avail_out == 0);
	
	return nRes;
}

int ZStream::ZFinish()
{
	return (_canRead ? ZLibInflateCleanUp() : ZLibDeflateCleanUp());
}

void ZStream::SetLength(__int64 value)
{
	_stream->SetLength(value);
}

void ZStream::Close()
{
	Dispose(true);
}


By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
Italy Italy
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions