Fix for bug 2283 (RVCT 4.0 support is missing from PDK 3.0.h)
Have multiple extension sections in the bld.inf, one for each version
of the compiler. The RVCT version building the tools will build the
runtime libraries for its version, but make sure we extract all the other
versions from zip archives. Also add the archive for RVCT4.
// 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;
}