toolsandutils/e32tools/elf2e32/source/deflatecompress.cpp
author Mike Kinghan <mikek@symbian.org>
Tue, 16 Nov 2010 14:32:12 +0000
branchGCC_SURGE
changeset 79 f7dee603db09
parent 61 b376866b09e6
permissions -rw-r--r--
[GCCE] We need a way for the HAL config extension to parameterise the HAL config file (.hcf) that will be used, depending upon the toolchain we are building with. E.g. if we are building BeagleBoard with RVCT we can configure hardware floating point because we have ARM's vfp math libraries; if we are building it with GCC, we lack this library support.

// Copyright (c) 2004-2009 Nokia Corporation and/or its subsidiary(-ies).
// All rights reserved.
// This component and the accompanying materials are made available
// under the terms of "Eclipse Public License v1.0"
// which accompanies this distribution, and is available
// at the URL "http://www.eclipse.org/legal/epl-v10.html".
//
// Initial Contributors:
// Nokia Corporation - initial contribution.
//
// Contributors:
//
// Description:
//

#include <fstream>
#include <cassert>

#include "e32imagedefs.h"
#include "errorhandler.h"
#include "farray.h"
#include "huffman.h"

const TInt KDeflateMinLength=3;
const TInt KDeflateMaxLength=KDeflateMinLength-1 + (1<<KDeflateLengthMag);
const TInt KDeflateMaxDistance=(1<<KDeflateDistanceMag);

// hashing
const TUint KDeflateHashMultiplier=0xAC4B9B19u;
const TInt KDeflateHashShift=24;

/**
Class HDeflateHash
@internalComponent
@released
*/
class HDeflateHash
{
	public:
		inline static HDeflateHash* NewLC(TInt aLinks);
//
		inline TInt First(const TUint8* aPtr,TInt aPos);
		inline TInt Next(TInt aPos,TInt aOffset) const;
	private:
		inline HDeflateHash();
		inline static TInt Hash(const TUint8* aPtr);
	private:
		typedef TUint16 TOffset;
	private:
		TInt iHash[256];
		TOffset iOffset[1];	// or more
};

/**
Class MDeflater
@internalComponent
@released
*/
class MDeflater
{
	public:
		void DeflateL(const TUint8* aBase,TInt aLength);
	private:
		const TUint8* DoDeflateL(const TUint8* aBase,const TUint8* aEnd,HDeflateHash& aHash);
		static TInt Match(const TUint8* aPtr,const TUint8* aEnd,TInt aPos,HDeflateHash& aHas);
		void SegmentL(TInt aLength,TInt aDistance);
		virtual void LitLenL(TInt aCode) =0;
		virtual void OffsetL(TInt aCode) =0;
		virtual void ExtraL(TInt aLen,TUint aBits) =0;
};

/**
Class TDeflateStats
@internalComponent
@released
*/
class TDeflateStats : public MDeflater
{
	public:
		inline TDeflateStats(TEncoding& aEncoding);
	private:
		// from MDeflater
		void LitLenL(TInt aCode);
		void OffsetL(TInt aCode);
		void ExtraL(TInt aLen,TUint aBits);
	private:
		TEncoding& iEncoding;
};

/**
Class TDeflater
@internalComponent
@released
*/
class TDeflater : public MDeflater
{
	public:
		inline TDeflater(TBitOutput& aOutput,const TEncoding& aEncoding);
	private:
		// from MDeflater
		void LitLenL(TInt aCode);
		void OffsetL(TInt aCode);
		void ExtraL(TInt aLen,TUint aBits);
	private:
		TBitOutput& iOutput;
		const TEncoding& iEncoding;
};


/**
Constructor for class HDeflateHash
@internalComponent
@released
*/
inline HDeflateHash::HDeflateHash()
{TInt* p=iHash+256;do *--p=-KDeflateMaxDistance-1; while (p>iHash);}

/**
@Leave - OutOfMemory
This function allocates memory for HDeflateHash
@param aLinks
@return pointer to allocated memory
@internalComponent
@released
*/
inline HDeflateHash* HDeflateHash::NewLC(TInt aLinks)
{
	//return new(HMem::Alloc(0,_FOFF(HDeflateHash,iOffset[Min(aLinks,KDeflateMaxDistance)]))) HDeflateHash;
	return new(new char[_FOFF(HDeflateHash,iOffset[Min(aLinks,KDeflateMaxDistance)])]) HDeflateHash;
}

/**
Hash function for HDeflateHash
@param aPtr
@return Hash value
@internalComponent
@released
*/
inline TInt HDeflateHash::Hash(const TUint8* aPtr)
{
	TUint x=aPtr[0]|(aPtr[1]<<8)|(aPtr[2]<<16);
	return (x*KDeflateHashMultiplier)>>KDeflateHashShift;
}

/**
Function First
@param aPtr
@param aPos
@internalComponent
@released
*/
inline TInt HDeflateHash::First(const TUint8* aPtr,TInt aPos)
{
	TInt h=Hash(aPtr);
	TInt offset=Min(aPos-iHash[h],KDeflateMaxDistance<<1);
	iHash[h]=aPos;
	iOffset[aPos&(KDeflateMaxDistance-1)]=TOffset(offset);
	return offset;
}

/**
Function Next
@param aPtr
@param aPos
@internalComponent
@released
*/
inline TInt HDeflateHash::Next(TInt aPos,TInt aOffset) const
{return aOffset+iOffset[(aPos-aOffset)&(KDeflateMaxDistance-1)];}


// Class TDeflater
//
// generic deflation algorithm, can do either statistics and the encoder

/**
Function Match
@param aPtr
@param aEnd
@param aPos
@param aHash
@internalComponent
@released
*/
TInt MDeflater::Match(const TUint8* aPtr,const TUint8* aEnd,TInt aPos,HDeflateHash& aHash)
{
	TInt offset=aHash.First(aPtr,aPos);
	if (offset>KDeflateMaxDistance)
		return 0;
	TInt match=0;
	aEnd=Min(aEnd,aPtr+KDeflateMaxLength);
	TUint8 c=*aPtr;
	do
	{
		const TUint8* p=aPtr-offset;
		if (p[match>>16]==c)
		{	// might be a better match
			const TUint8* m=aPtr;
			for (;;)
			{
				if (*p++!=*m++)
					break;
				if (m<aEnd)
					continue;
				return ((m-aPtr)<<16)|offset;
			}
			TInt l=m-aPtr-1;
			if (l>match>>16)
			{
				match=(l<<16)|offset;
				c=m[-1];
			}
		}
		offset=aHash.Next(aPos,offset);
	} while (offset<=KDeflateMaxDistance);
	return match;
}

/*
Apply the deflation algorithm to the data [aBase,aEnd)
Return a pointer after the last byte that was deflated (which may not be aEnd)
@param aBase
@param aEnd
@param aHash
@internalComponent
@released
*/
const TUint8* MDeflater::DoDeflateL(const TUint8* aBase,const TUint8* aEnd,HDeflateHash& aHash)
{
	const TUint8* ptr=aBase;
	TInt prev=0;		// the previous deflation match
	do
	{
		TInt match=Match(ptr,aEnd,ptr-aBase,aHash);
// Extra deflation applies two optimisations which double the time taken
// 1. If we have a match at p, then test for a better match at p+1 before using it
// 2. When we have a match, add the hash links for all the data which will be skipped 
		if (match>>16 < prev>>16)
		{	// use the previous match--it was better
			TInt len=prev>>16;
			SegmentL(len,prev-(len<<16));
			// fill in missing hash entries for better compression
			const TUint8* e=ptr+len-2;
			do
			{
				++ptr;
				if (ptr + 2 < aEnd)
				  aHash.First(ptr,ptr-aBase);
			} while (ptr<e);
			prev=0;
		}
		else if (match<=(KDeflateMinLength<<16))
			LitLenL(*ptr);			// no deflation match here
		else
		{	// save this match and test the next position
			if (prev)	// we had a match at ptr-1, but this is better
				LitLenL(ptr[-1]);
			prev=match;
		}
		++ptr;
	} while (ptr+KDeflateMinLength-1<aEnd);
	if (prev)
	{		// emit the stored match
		TInt len=prev>>16;
		SegmentL(len,prev-(len<<16));
		ptr+=len-1;
	}
	return ptr;
}

/*
The generic deflation algorithm
@param aBase
@param aLength
@internalComponent
@released
*/
void MDeflater::DeflateL(const TUint8* aBase,TInt aLength)
{
	const TUint8* end=aBase+aLength;
	if (aLength>KDeflateMinLength)
	{	// deflation kicks in if there is enough data
		HDeflateHash* hash=HDeflateHash::NewLC(aLength);

		aBase=DoDeflateL(aBase,end,*hash);
		delete hash;
	}
	while (aBase<end)					// emit remaining bytes
		LitLenL(*aBase++);
	LitLenL(TEncoding::EEos);	// eos marker
}

/*
Turn a (length,offset) pair into the deflation codes+extra bits before calling the specific
LitLen(), Offset() and Extra() functions.
@param aLength
@param aDistance
@internalComponent
@released
*/
void MDeflater::SegmentL(TInt aLength,TInt aDistance)
{
	aLength-=KDeflateMinLength;
	TInt extralen=0;
	TUint len=aLength;
	while (len>=8)
	{
		++extralen;
		len>>=1;
	}
	LitLenL((extralen<<2)+len+TEncoding::ELiterals);
	if (extralen)
		ExtraL(extralen,aLength);
//
	aDistance--;
	extralen=0;
	TUint dist=aDistance;
	while (dist>=8)
	{
		++extralen;
		dist>>=1;
	}
	OffsetL((extralen<<2)+dist);
	if (extralen)
		ExtraL(extralen,aDistance);
}

/**
Class TDeflateStats
This class analyses the data stream to generate the frequency tables 
for the deflation algorithm
@internalComponent
@released
*/
inline TDeflateStats::TDeflateStats(TEncoding& aEncoding)
	:iEncoding(aEncoding)
	{}
/*
Function LitLenL
@Leave
@param aCode
@internalComponent
@released
*/
void TDeflateStats::LitLenL(TInt aCode)
	{
	++iEncoding.iLitLen[aCode];
	}

/*
@Leave ArrayIndexOutOfBounds
Finction OffsetL
@param aCode
@internalComponent
@released
*/
void TDeflateStats::OffsetL(TInt aCode)
	{
	++iEncoding.iDistance[aCode];
	}

/*
Function ExtraL
@Leave
@internalComponent
@released
*/void TDeflateStats::ExtraL(TInt,TUint)
	{}

/**
Constructor of Class TDeflater
Extends MDeflater to provide huffman encoding of the output
@internalComponent
@released
*/
inline TDeflater::TDeflater(TBitOutput& aOutput,const TEncoding& aEncoding)
//
// construct for encoding
//
	:iOutput(aOutput),iEncoding(aEncoding)
	{}

/*
Function LitLenL
@Leave
@param aCode 
@internalComponent
@released
*/
void TDeflater::LitLenL(TInt aCode)
	{
	iOutput.HuffmanL(iEncoding.iLitLen[aCode]);
	}

/*
Function OffsetL
@Leave
@param aCdoe 
@internalComponent
@released
*/
void TDeflater::OffsetL(TInt aCode)
	{
	iOutput.HuffmanL(iEncoding.iDistance[aCode]);
	}

/*
Function ExtraL
@Leave
@param  aLen
@param aBits
@internalComponent
@released
*/
void TDeflater::ExtraL(TInt aLen,TUint aBits)
	{
	iOutput.WriteL(aBits,aLen);
	}
/*
Function DoDeflateL
@Leave
@param aBuf
@param aLength
@param aOutput
@param aEncoding
@internalComponent
@released
*/
void DoDeflateL(const TUint8* aBuf,TInt aLength,TBitOutput& aOutput,TEncoding& aEncoding)
	{
// analyse the data for symbol frequency 
	TDeflateStats analyser(aEncoding);
	analyser.DeflateL(aBuf,aLength);
	
// generate the required huffman encodings
	Huffman::HuffmanL(aEncoding.iLitLen,TEncoding::ELitLens,aEncoding.iLitLen);
	Huffman::HuffmanL(aEncoding.iDistance,TEncoding::EDistances,aEncoding.iDistance);

// Store the encoding table
	Huffman::ExternalizeL(aOutput,aEncoding.iLitLen,KDeflationCodes);

// generate the tables
	Huffman::Encoding(aEncoding.iLitLen,TEncoding::ELitLens,aEncoding.iLitLen);
	Huffman::Encoding(aEncoding.iDistance,TEncoding::EDistances,aEncoding.iDistance);

// now finally deflate the data with the generated encoding
	TDeflater deflater(aOutput,aEncoding);
	deflater.DeflateL(aBuf,aLength);
	aOutput.PadL(1);
	}

/*
Function DeflateL
@Leave
@param aBuf
@param aLength
@param aOutput 
@internalComponent
@released
*/
void DeflateL(const TUint8* aBuf, TInt aLength, TBitOutput& aOutput)
	{
	TEncoding* encoding=new TEncoding;
	memset(encoding,0,sizeof(TEncoding));
	DoDeflateL(aBuf,aLength,aOutput,*encoding);
	delete encoding;
	}
/*
Function DeflateCompress
@param bytes
@param size
@param os
@internalComponent
@released
*/
void DeflateCompress(char *bytes,size_t size, std::ofstream & os)
	{
	TFileOutput* output=new TFileOutput(os);
	output->iDataCount = 0;
	DeflateL((TUint8*)bytes,size,*output);
	output->FlushL();
	delete output;
	}