// 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 the License "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:
// e32test/buffer/t_huff.cpp
// Overview:
// Test methods of the Huffman, TBitInput and TBitOutput classes.
// API Information:
// Huffman, TBitInput, TBitOutput
// Details:
// - Test and verify the results of TBitInput bit reading:
// - test and verify single bit reads, multiple bit reads and 32-bit reads
// - test and verify single bit reads and multiple bit reads from a
// fractured input.
// - test and verify overrun reads
// - Test and verify the results of TBitOutput bit writing:
// - test and verify bitstream padding
// - test and verify single bit and multiple bit writes
// - test and verify overflow writes
// - Test and verify the results of a Huffman decoder using Huffman class
// static methods, TBitOutput and TBitInput objects.
// - Test and verify the results of a Huffman generator for known distributions:
// flat, power-of-2 and Fibonacci.
// - Test and verify the results of a Huffman generator for random distributions:
// - generate random frequency distributions and verify:
// (a) the Huffman generator creates a mathematically 'optimal code'
// (b) the canonical encoding is canonical
// (c) the decoding tree correctly decodes each code
// (d) the encoding can be correctly externalised and internalised
// Platforms/Drives/Compatibility:
// All
// Assumptions/Requirement/Pre-requisites:
// Failures and causes:
// Base Port information:
//
//
#include <e32test.h>
#include <e32math.h>
#include <e32huffman.h>
RTest test(RProcess().FileName());
const Uint64 KTestData=UI64LIT(0x6f1b09a7e8c523d4);
const TUint8 KTestBuffer[] = {0x6f,0x1b,0x09,0xa7,0xe8,0xc5,0x23,0xd4};
const TInt KTestBytes=sizeof(KTestBuffer);
const TInt KTestBits=KTestBytes*8;
// Input stream: bit and multi-bit read tests with exhsautive buffer reload testing
typedef TBool (*TestFn)(TBitInput& aIn, Uint64 aBits, TInt aCount);
class TAlignedBitInput : public TBitInput
{
public:
TAlignedBitInput(const TUint8*,TInt,TInt);
private:
void UnderflowL();
private:
const TUint8* iRemainder;
TInt iCount;
};
TAlignedBitInput::TAlignedBitInput(const TUint8* aPtr,TInt aCount,TInt aOffset)
:TBitInput(aPtr,32-aOffset,aOffset), iRemainder(aPtr+4), iCount(aOffset+aCount-32)
{}
void TAlignedBitInput::UnderflowL()
{
if (!iRemainder)
User::Leave(KErrUnderflow);
else
{
Set(iRemainder,iCount);
iRemainder=0;
}
}
class TSplitBitInput : public TBitInput
{
public:
TSplitBitInput(const TUint8*,TInt,TInt,TInt);
private:
void UnderflowL();
private:
const TUint8* iBase;
TInt iBlockSize;
TInt iOffset;
TInt iAvail;
};
TSplitBitInput::TSplitBitInput(const TUint8* aPtr,TInt aLength,TInt aOffset,TInt aSize)
:TBitInput(aPtr,aSize,aOffset), iBase(aPtr), iBlockSize(aSize), iOffset(aOffset+aSize), iAvail(aLength-aSize)
{}
void TSplitBitInput::UnderflowL()
{
TInt len=Min(iBlockSize,iAvail);
if (len==0)
User::Leave(KErrUnderflow);
Set(iBase,len,iOffset);
iOffset+=len;
iAvail-=len;
}
class TAlternateBitInput : public TBitInput
{
public:
TAlternateBitInput(const TUint8*,TInt,TInt);
private:
void UnderflowL();
private:
const TUint8* iBase;
TInt iOffset;
TInt iAvail;
};
TAlternateBitInput::TAlternateBitInput(const TUint8* aPtr,TInt aLength,TInt aOffset)
:TBitInput(aPtr,1,aOffset), iBase(aPtr), iOffset(aOffset+2), iAvail(aLength-2)
{}
void TAlternateBitInput::UnderflowL()
{
if (iAvail<=0)
User::Leave(KErrUnderflow);
Set(iBase,1,iOffset);
iOffset+=2;
iAvail-=2;
}
void TestReader(TBitInput& aIn, TestFn aFunc, Uint64 aBits, TInt aCount)
{
TBool eof=EFalse;
TRAPD(r,eof=aFunc(aIn,aBits,aCount));
test (r==KErrNone);
if (eof)
{
TRAP(r,aIn.ReadL());
test (r==KErrUnderflow);
}
}
void TestBits(TInt aOffset, TInt aCount, TestFn aFunc)
{
Uint64 bits=KTestData;
if (aOffset)
bits<<=aOffset;
if (aCount<64)
bits&=~((Uint64(1)<<(64-aCount))-1);
// test with direct input
TBitInput in1(KTestBuffer,aCount,aOffset);
TestReader(in1,aFunc,bits,aCount);
// test with aligned input
if (aOffset<32 && aOffset+aCount>32)
{
TAlignedBitInput in2(KTestBuffer,aCount,aOffset);
TestReader(in2,aFunc,bits,aCount);
}
// test with blocked input
for (TInt block=aCount;--block>0;)
{
TSplitBitInput in3(KTestBuffer,aCount,aOffset,block);
TestReader(in3,aFunc,bits,aCount);
}
}
void TestAlternateBits(TInt aOffset, TInt aCount, TestFn aFunc)
{
Uint64 bits=0;
TInt c=0;
for (TInt ix=aOffset;ix<aOffset+aCount;ix+=2)
{
if (KTestData<<ix>>63)
bits|=Uint64(1)<<(63-c);
++c;
}
// test with alternate input
TAlternateBitInput in1(KTestBuffer,aCount,aOffset);
TestReader(in1,aFunc,bits,c);
}
void PermBits(TestFn aFunc, TInt aMinCount=1, TInt aMaxCount=64)
{
for (TInt offset=0;offset<KTestBits;++offset)
for (TInt count=Min(KTestBits-offset,aMaxCount);count>=aMinCount;--count)
TestBits(offset,count,aFunc);
}
void AlternateBits(TestFn aFunc, TInt aMinCount=1)
{
for (TInt offset=0;offset<KTestBits;++offset)
for (TInt count=KTestBits-offset;count>=aMinCount;--count)
TestAlternateBits(offset,count,aFunc);
}
TBool SingleBitRead(TBitInput& aIn, Uint64 aBits, TInt aCount)
{
while (--aCount>=0)
{
test (aIn.ReadL() == (aBits>>63));
aBits<<=1;
}
return ETrue;
}
TBool MultiBitRead(TBitInput& aIn, Uint64 aBits, TInt aCount)
{
TInt c=aCount/2;
TUint v=aIn.ReadL(c);
if (c==0)
test (v==0);
else
{
test (v==TUint(aBits>>(64-c)));
aBits<<=c;
}
c=aCount-c;
v=aIn.ReadL(c);
if (c==0)
test (v==0);
else
test (v==TUint(aBits>>(64-c)));
return ETrue;
}
TBool LongShortRead(TBitInput& aIn, Uint64 aBits, TInt aCount)
{
TUint v=aIn.ReadL(32);
test (v==TUint(aBits>>32));
aBits<<=32;
TInt c=aCount-32;
v=aIn.ReadL(c);
if (c==0)
test (v==0);
else
test (v==TUint(aBits>>(64-c)));
return ETrue;
}
TBool ShortLongRead(TBitInput& aIn, Uint64 aBits, TInt aCount)
{
TInt c=aCount-32;
TUint v=aIn.ReadL(c);
if (c==0)
test (v==0);
else
{
test (v==TUint(aBits>>(64-c)));
aBits<<=c;
}
v=aIn.ReadL(32);
test (v==TUint(aBits>>32));
return ETrue;
}
TBool EofRead(TBitInput& aIn, Uint64, TInt aCount)
{
TRAPD(r,aIn.ReadL(aCount+1));
test(r==KErrUnderflow);
return EFalse;
}
void TestBitReading()
{
test.Start(_L("Test single bit reads"));
PermBits(&SingleBitRead);
test.Next(_L("Test multi bit reads"));
PermBits(&MultiBitRead);
test.Next(_L("Test 32-bit reads"));
PermBits(&LongShortRead,32);
PermBits(&ShortLongRead,32);
test.Next(_L("Test single bit reads (fractured input)"));
AlternateBits(&SingleBitRead);
test.Next(_L("Test multi bit reads (fractured input)"));
AlternateBits(&MultiBitRead);
test.Next(_L("Test overrun reads"));
PermBits(&EofRead,1,31);
test.End();
}
// Bit output testing (assumes bit input is correct)
void TestPadding()
{
TUint8 buffer[4];
TBitOutput out(buffer,4);
test(out.Ptr()==buffer);
test(out.BufferedBits()==0);
out.PadL(0);
test(out.Ptr()==buffer);
test(out.BufferedBits()==0);
out.WriteL(0,0);
out.PadL(0);
test(out.Ptr()==buffer);
test(out.BufferedBits()==0);
TInt i;
for (i=1;i<=8;++i)
{
out.Set(buffer,4);
out.WriteL(0,i);
test(out.BufferedBits()==(i%8));
out.PadL(1);
test(out.BufferedBits()==0);
out.WriteL(0,i);
test(out.BufferedBits()==(i%8));
out.PadL(1);
test(out.BufferedBits()==0);
test (out.Ptr()==buffer+2);
test (buffer[0]==(0xff>>i));
test (buffer[1]==(0xff>>i));
}
for (i=1;i<=8;++i)
{
out.Set(buffer,4);
out.WriteL(0xff,i);
out.PadL(0);
test (out.Ptr()==buffer+1);
test (buffer[0]==(0xff^(0xff>>i)));
}
}
void TestBitWrites()
{
TUint8 buffer[KTestBytes];
TBitOutput out(buffer,KTestBytes);
TBitInput in(KTestBuffer,KTestBits);
TInt i;
for (i=KTestBits;--i>=0;)
out.WriteL(in.ReadL(),1);
test (Mem::Compare(buffer,KTestBytes,KTestBuffer,KTestBytes)==0);
Mem::FillZ(buffer,KTestBytes);
out.Set(buffer,KTestBytes);
Uint64 bits=KTestData;
for (i=KTestBits;--i>=0;)
out.WriteL(TUint(bits>>i),1);
test (Mem::Compare(buffer,KTestBytes,KTestBuffer,KTestBytes)==0);
}
void TestMultiBitWrites()
{
TInt i=0;
for (TInt j=0;j<32;++j)
for (TInt k=0;k<32;++k)
{
++i;
if (i+j+k>KTestBits)
i=0;
TUint8 buffer[KTestBytes];
TBitInput in(KTestBuffer,KTestBits);
TBitOutput out(buffer,KTestBytes);
in.ReadL(i);
out.WriteL(in.ReadL(j),j);
out.WriteL(in.ReadL(k),k);
out.PadL(0);
const TUint8* p=out.Ptr();
test (p-buffer == (j+k+7)/8);
Uint64 v=0;
while (p>buffer)
v=(v>>8) | Uint64(*--p)<<56;
Uint64 res=KTestData;
if (i+j+k<KTestBits)
res>>=KTestBits-i-j-k;
if (j+k<KTestBits)
res<<=KTestBits-j-k;
test (v==res);
}
}
void TestAlternatingWrites()
{
const TInt KBufferSize=(1+32)*32;
TUint8 buffer[(7+KBufferSize)/8];
TBitOutput out(buffer,sizeof(buffer));
TInt i;
for (i=0;i<=32;++i)
out.WriteL(i&1?0xffffffff:0,i);
while (--i>=0)
out.WriteL(i&1?0:0xffffffff,i);
out.PadL(0);
TBitInput in(buffer,KBufferSize);
for (i=0;i<=32;++i)
{
TUint v=in.ReadL(i);
if (i&1)
test (v == (1u<<i)-1u);
else
test (v == 0);
}
while (--i>=0)
{
TUint v=in.ReadL(i);
if (i&1)
test (v == 0);
else if (i==32)
test (v == 0xffffffffu);
else
test (v == (1u<<i)-1u);
}
}
class TOverflowOutput : public TBitOutput
{
public:
TOverflowOutput();
private:
void OverflowL();
private:
TUint8 iBuf[1];
TInt iIx;
};
TOverflowOutput::TOverflowOutput()
:iIx(0)
{}
void TOverflowOutput::OverflowL()
{
if (Ptr()!=0)
{
test (Ptr()-iBuf == 1);
test (iBuf[0] == KTestBuffer[iIx]);
if (++iIx==KTestBytes)
User::Leave(KErrOverflow);
}
Set(iBuf,1);
}
void OverflowTestL(TBitOutput& out, TInt j)
{
for (;;) out.WriteL(0xffffffffu,j);
}
void TestOverflow()
{
test.Start(_L("Test default constructed output"));
TBitOutput out;
TInt i;
for (i=1;i<=8;++i)
{
TRAPD(r,out.WriteL(1,1));
if (i<8)
{
test (out.BufferedBits() == i);
test (r == KErrNone);
}
else
test (r == KErrOverflow);
}
test.Next(_L("Test overflow does not overrun the buffer"));
i=0;
for (TInt j=1;j<=32;++j)
{
if (++i>KTestBytes)
i=1;
TUint8 buffer[KTestBytes+1];
Mem::FillZ(buffer,sizeof(buffer));
out.Set(buffer,i);
TRAPD(r,OverflowTestL(out,j));
test (r == KErrOverflow);
TInt k=0;
while (buffer[k]==0xff)
{
++k;
test (k<TInt(sizeof(buffer)));
}
test (k <= i);
test ((i-k)*8 < j);
while (k<TInt(sizeof(buffer)))
{
test (buffer[k]==0);
++k;
}
}
test.Next(_L("Test overflow handler"));
TOverflowOutput vout;
TBitInput in(KTestBuffer,KTestBits);
for (i=KTestBits;--i>=0;)
vout.WriteL(in.ReadL(),1);
test(vout.BufferedBits() == 0);
TRAPD(r,vout.WriteL(0,1));
test (r == KErrNone);
TRAP(r,vout.PadL(0));
test (r == KErrOverflow);
test.End();
}
void TestBitWriting()
{
test.Start(_L("Test padding"));
TestPadding();
test.Next(_L("Test bit writes"));
TestBitWrites();
test.Next(_L("Test multi-bit writes"));
TestMultiBitWrites();
TestAlternatingWrites();
test.Next(_L("Test overflow writes"));
TestOverflow();
test.End();
}
// Huffman decode testing
#ifdef __ARMCC__
#pragma Onoinline
#endif
void Dummy(volatile TInt & /*x*/)
{
}
void TestHuffmanL()
{
const TInt KTestBits=32*32;
// build the huffman decoding tree for
// 0: '0'
// 1: '10'
// 2: '110' etc
TUint32 huffman[Huffman::KMaxCodeLength+1];
TInt i;
for (i=0;i<Huffman::KMaxCodeLength;++i)
huffman[i]=i+1;
huffman[Huffman::KMaxCodeLength]=Huffman::KMaxCodeLength;
Huffman::Decoding(huffman,Huffman::KMaxCodeLength+1,huffman);
TUint8 buffer[KTestBits/8];
for (TInt sz=0;sz<Huffman::KMaxCodeLength;++sz)
{
const TInt rep=KTestBits/(sz+1);
TBitOutput out(buffer,sizeof(buffer));
for (i=0;i<rep;++i)
{
out.WriteL(0xffffffff,sz);
out.WriteL(0,1);
}
out.PadL(1);
for (TInt blk=1;blk<=64;++blk)
{
TSplitBitInput in(buffer,rep*(sz+1)-1,0,blk);
for (i=0;i<rep-1;++i)
{
TInt v=-1;
TRAPD(r,v=in.HuffmanL(huffman));
test (r==KErrNone);
test (sz==v);
}
volatile TInt v=-1;
Dummy(v);
TRAPD(r, v=in.HuffmanL(huffman));
test (v==-1);
test (r==KErrUnderflow);
}
}
}
// Huffman generator testing with known but atypical distributions
void FlatHuffman(TInt aMaxCount)
{
TUint32* tab=new TUint32[aMaxCount];
test (tab!=NULL);
// test empty distribution
Mem::FillZ(tab,sizeof(TUint32)*aMaxCount);
TRAPD(r, Huffman::HuffmanL(tab,aMaxCount,tab));
test (r==KErrNone);
TInt i;
for (i=0;i<aMaxCount;++i)
test (tab[i]==0);
Huffman::Decoding(tab,aMaxCount,tab);
// test single-symbol distribution
Mem::FillZ(tab,sizeof(TUint32)*aMaxCount);
tab[0]=100;
TRAP(r, Huffman::HuffmanL(tab,aMaxCount,tab));
test (r==KErrNone);
test (tab[0]==1);
for (i=1;i<aMaxCount;++i)
test (tab[i]==0);
Huffman::Decoding(tab,aMaxCount,tab,200);
TUint8 bits=0;
TBitInput in(&bits,1);
test (in.HuffmanL(tab)==200);
// test flat distributions with 2..aMaxCount symbols
TInt len=0;
for (TInt c=2;c<aMaxCount;++c)
{
if ((2<<len)==c)
++len;
Mem::FillZ(tab,sizeof(TUint32)*aMaxCount);
for (i=0;i<c;++i)
tab[i]=100;
TRAP(r, Huffman::HuffmanL(tab,aMaxCount,tab));
test (r==KErrNone);
TInt small=0;
for (i=0;i<c;++i)
{
if (TInt(tab[i])==len)
++small;
else
test (TInt(tab[i])==len+1);
}
for (;i<aMaxCount;++i)
test (tab[i]==0);
test (small == (2<<len)-c);
}
delete [] tab;
}
void Power2Huffman()
//
// Test Huffman generator for the distribution 2^0,2^0,2^1,2^2,2^3,...
//
{
TUint32 tab[Huffman::KMaxCodeLength+2];
for (TInt c=1;c<=Huffman::KMaxCodeLength+1;c++)
{
tab[c]=tab[c-1]=1;
TInt i;
for (i=c-1;--i>=0;)
tab[i]=2*tab[i+1];
TRAPD(r,Huffman::HuffmanL(tab,c+1,tab));
if (c>Huffman::KMaxCodeLength)
{
test (r==KErrOverflow);
continue;
}
test (TInt(tab[c]) == c);
for (i=0;i<c;++i)
test (TInt(tab[i]) == i+1);
Huffman::Decoding(tab,c+1,tab);
for (i=0;i<=c;++i)
{
TUint8 buf[4];
TBitOutput out(buf,4);
out.WriteL(0xffffffff,i);
out.WriteL(0,1);
out.PadL(1);
TBitInput in(buf,Min(i+1,c));
TInt ix=-1;
TRAP(r, ix=in.HuffmanL(tab));
test (r==KErrNone);
test (ix==i);
TRAP(r, in.HuffmanL(tab));
test (r==KErrUnderflow);
}
}
}
void FibonacciHuffman()
//
// Test Huffman generator for the distribution 1,1,2,3,5,8,13,21,...
//
{
TUint32 tab[Huffman::KMaxCodeLength+2];
for (TInt c=1;c<=Huffman::KMaxCodeLength+1;c++)
{
tab[c]=tab[c-1]=1;
TInt i;
for (i=c-1;--i>=0;)
tab[i]=tab[i+1]+tab[i+2];
TRAPD(r,Huffman::HuffmanL(tab,c+1,tab));
if (c>Huffman::KMaxCodeLength)
{
test (r==KErrOverflow);
continue;
}
test (TInt(tab[c]) == c);
for (i=0;i<c;++i)
test (TInt(tab[i]) == i+1);
Huffman::Decoding(tab,c+1,tab);
for (i=0;i<=c;++i)
{
TUint8 buf[4];
TBitOutput out(buf,4);
out.WriteL(0xffffffff,i);
out.WriteL(0,1);
out.PadL(1);
TBitInput in(buf,Min(i+1,c));
TInt ix=-1;
TRAP(r, ix=in.HuffmanL(tab));
test (r==KErrNone);
test (ix==i);
TRAP(r, in.HuffmanL(tab));
test (r==KErrUnderflow);
}
}
}
void SpecificHuffman(TInt aMaxCount)
{
test.Start(_L("Flat distributions"));
FlatHuffman(aMaxCount);
test.Next(_L("Power-of-2 distributions"));
Power2Huffman();
test.Next(_L("Fibonacci distributions"));
FibonacciHuffman();
test.End();
}
// Huffman generator validity testing. Checking code properties for a sequence of random
// frequency distributions.
TInt64 RSeed(KTestData);
inline TInt Random(TInt aLimit)
{return aLimit>0 ? (Math::Rand(RSeed)%aLimit) : 0;}
void GenerateFreq(TUint32* aTable, TInt aCount, TInt aTotalFreq, TInt aVariance, TInt aZeros)
//
// Generate a random frequency table
//
{
for (TInt i=0;i<aCount;++i)
{
if (aZeros && Random(aCount-i)<aZeros)
{
aTable[i]=0;
--aZeros;
}
else if (aCount-aZeros-i == 1)
{
aTable[i]=aTotalFreq;
aTotalFreq=0;
}
else
{
TInt ave=aTotalFreq/(aCount-aZeros-i);
if (aVariance==0)
{
aTable[i]=ave;
aTotalFreq-=ave;
}
else
{
TInt var=I64INT(TInt64(ave)<<aVariance>>8);
TInt min=Max(1,ave-var);
TInt max=Min(1+aTotalFreq-(aCount-aZeros-i),ave+var);
TInt f = max<=min ? ave : min+Random(max-min);
aTable[i] = f;
aTotalFreq-=f;
}
}
}
}
TInt NumericalSort(const TUint32& aLeft, const TUint32& aRight)
{
return aLeft-aRight;
}
TInt64 VerifyOptimalCode(const TUint32* aFreq, const TUint32* aCode, TInt aCount, TInt aTotalFreqLog2)
//
// We can show tht the expected code length is at least as short as a Shannon-Fano encoding
//
{
TInt64 totalHuff=0;
TInt64 totalSF=0;
TInt i;
for (i=0;i<aCount;++i)
{
TInt f=aFreq[i];
TInt l=aCode[i];
if (f == 0)
{
test (l == 0);
continue;
}
totalHuff+=f*l;
TInt s=1;
while ((f<<s>>aTotalFreqLog2)!=1)
++s;
totalSF+=f*s;
}
test (totalHuff<=totalSF);
RPointerArray<TUint32> index(aCount);
CleanupClosePushL(index);
for (i=0;i<aCount;++i)
{
if (aFreq[i] != 0)
User::LeaveIfError(index.InsertInOrderAllowRepeats(aFreq+i,&NumericalSort));
}
TInt smin,smax;
smin=smax=aCode[index[0]-aFreq];
for (i=1;i<index.Count();++i)
{
TInt pix=index[i-1]-aFreq;
TInt nix=index[i]-aFreq;
TInt pf=aFreq[pix];
TInt nf=aFreq[nix];
TInt ps=aCode[pix];
TInt ns=aCode[nix];
if (nf==pf)
{
smin=Min(smin,ns);
smax=Max(smax,ns);
test (smin==smax || smin+1==smax);
}
else
{
test (nf>pf);
test (ns<=ps);
smin=smax=ns;
}
}
CleanupStack::PopAndDestroy();
return totalHuff;
}
TInt LexicalSort(const TUint32& aLeft, const TUint32& aRight)
{
const TUint32 KCodeMask=(1<<Huffman::KMaxCodeLength)-1;
return (aLeft&KCodeMask)-(aRight&KCodeMask);
}
void VerifyCanonicalEncodingL(const TUint32* aCode, const TUint32* aEncode, TInt aCount)
//
// A canonical encoding assigns codes from '0' in increasing code length order, and
// in increasing index in the table for equal code length.
//
// Huffman is also a 'prefix-free' code, so we check this property of the encoding
//
{
TInt i;
for (i=0;i<aCount;++i)
test (aCode[i] == aEncode[i]>>Huffman::KMaxCodeLength);
RPointerArray<TUint32> index(aCount);
CleanupClosePushL(index);
for (i=0;i<aCount;++i)
{
if (aCode[i] != 0)
User::LeaveIfError(index.InsertInOrder(aEncode+i,&LexicalSort));
}
for (i=1;i<index.Count();++i)
{
TInt pix=index[i-1]-aEncode;
TInt nix=index[i]-aEncode;
test (aCode[pix]<=aCode[nix]); // code lengths are always increasing
test (aCode[pix]<aCode[nix] || pix<nix); // same code length => index order preserved
// check that a code is not a prefix of the next one. This is sufficent for checking the
// prefix condition as we have already sorted the codes in lexicographical order
TUint32 pc=aEncode[pix]<<(32-Huffman::KMaxCodeLength);
TUint32 nc=aEncode[nix]<<(32-Huffman::KMaxCodeLength);
TInt plen=aCode[pix];
test ((pc>>(32-plen)) != (nc>>(32-plen))); // pc is not a prefix for nc
}
CleanupStack::PopAndDestroy(&index);
}
void VerifyCanonicalDecoding(const TUint32* aEncode, const TUint32* aDecode, TInt aCount, TInt aBase)
//
// We've checked the encoding is valid, so now we check that the decoding can correctly
// decode every code
//
{
TUint8 buffer[(Huffman::KMaxCodeLength+7)/8];
TBitInput in;
TBitOutput out;
while (--aCount>=0)
{
if (aEncode[aCount])
{
out.Set(buffer,sizeof(buffer));
out.HuffmanL(aEncode[aCount]);
out.PadL(0);
in.Set(buffer,aEncode[aCount]>>Huffman::KMaxCodeLength);
TInt v=-1;
TRAPD(r,v=in.HuffmanL(aDecode));
test (r==KErrNone);
test (v==aCount+aBase);
TRAP(r,in.ReadL());
test (r==KErrUnderflow);
}
}
}
TInt TestExternalizeL(const TUint32* aCode, TUint8* aExtern, TUint32* aIntern, TInt aCount)
{
TBitOutput out(aExtern,aCount*4);
Huffman::ExternalizeL(out,aCode,aCount);
TInt bits=out.BufferedBits()+8*(out.Ptr()-aExtern);
out.PadL(0);
TBitInput in(aExtern,bits);
TRAPD(r,Huffman::InternalizeL(in,aIntern,aCount));
test (r == KErrNone);
test (Mem::Compare((TUint8*)aCode,aCount*sizeof(TUint32),(TUint8*)aIntern,aCount*sizeof(TUint32)) == 0);
TRAP(r,in.ReadL());
test (r == KErrUnderflow);
return bits;
}
void RandomHuffmanL(TInt aIter, TInt aMaxSymbols)
//
// generate random frequency distributions and verify
// (a) the Huffman generator creates a mathematically 'optimal code'
// (b) the canonical encoding is the canonical encoding
// (c) the decoding tree correctly decodes each code.
// (d) the encoding can be correctly externalised and internalised
//
{
TReal KLog2;
Math::Ln(KLog2,2);
const TInt KTotalFreqLog2=24;
const TInt KTotalFreq=1<<KTotalFreqLog2;
while (--aIter >= 0)
{
TInt num=2+Random(aMaxSymbols-1);
TUint32* const freq = new(ELeave) TUint32[num*3];
CleanupArrayDeletePushL(freq);
TUint32* const code = freq+num;
TUint32* const encoding = code+num;
TUint32* const decoding = freq;
TUint8* const exter = (TUint8*)encoding;
TUint32* const intern = freq;
TInt var=Random(24);
TInt zero=Random(num-2);
GenerateFreq(freq,num,KTotalFreq,var,zero);
Huffman::HuffmanL(freq,num,code);
VerifyOptimalCode(freq,code,num,KTotalFreqLog2);
Huffman::Encoding(code,num,encoding);
VerifyCanonicalEncodingL(code,encoding,num);
TInt base=Random(Huffman::KMaxCodes-num);
Huffman::Decoding(code,num,decoding,base);
VerifyCanonicalDecoding(encoding,decoding,num,base);
TestExternalizeL(code,exter,intern,num);
CleanupStack::PopAndDestroy();
}
}
///
void MainL()
{
test.Start(_L("Test Bit reader"));
TestBitReading();
test.Next(_L("Test Bit writer"));
TestBitWriting();
test.Next(_L("Test Huffman decoder"));
TestHuffmanL();
test.Next(_L("Test Huffman generator for known distributions"));
SpecificHuffman(800);
test.Next(_L("Test Huffman generator for random distributions"));
TRAPD(r,RandomHuffmanL(1000,800));
test (r==KErrNone);
test.End();
}
TInt E32Main()
{
test.Title();
CTrapCleanup* c=CTrapCleanup::New();
test (c!=0);
TRAPD(r,MainL());
test (r==KErrNone);
delete c;
test.Close();
return r;
}