///////////////////////////////////////////////////////////////////////////////
// LZWStream.h
#ifndef LZWStreamh
#define LZWStreamh
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
/*
This is a small suite of simple compression classes that use the LZWStream algorithm optionally using the GIF coding format.
There is a CLZWStreamEncoder class and a CLZWStreamDecoder class (both derived from CLZWStream).
You can specify a File or an area of memory for each class to act upon.
A file is specifed simply by its Path, a section of memory by its Base and Length.
The simplest way to use a CString to hold the data:
CString S("anna and nanna banned bananas and bandannas");
CLZWStreamEncoder E(S);
// S is now the compressed data (or the original data if no compression was possible).
int CompressedLength=S.GetLength(); // At this point S may contain NULLs before its end. GetLength returns the correct length for the compressed data.
CLZWStreamDecoder D(S);
//S is now back to the original data or empty if we ran out of memory or the data was corrupt.
if(S.IsEmpty() && CompressedLength) AfxMessageBox("LZWStream Decompression Failed");
You can use the
CLZWStreamEncoder Encoder;
const char* src="anna and nanna banned bananas and bandannas";
char LZWStream[64];
char dst[64];
Encoder.Encode(src, LZWStream, strlen(src)+1);
CLZWStreamDecoder Decoder;
Decoder.Decode(LZWStream, dst, Encoder.GetOutSize());
double Ratio=strlen(src)+1./Encoder.GetOutSize();
DeleteFile("ReadMeNow.txt");
CLZWStreamEncoder Encoder;
Encoder.Encode("ReadMe.txt", "ReadMe.LZWStream");
CLZWStreamDecoder Decoder;
Decoder.Decode("ReadMe.LZWStream", "ReadMeNow.txt");
*/
#ifndef Languageh
static const char* sBadFileName ("Bad File Name.");
static const char* sBadMAlloc ("Out of Memory (RAM).");
static const char* sBadDataCode ("Bad Data Code.");
static const char* sDictOverflow("LZWStream Dictionary overflowed.");
#endif
class CLZWStream {
public:
CLZWStream() : GIFEncoding(false), GotStoredByte(false), InFile(0), OutFile(0), InEnd(0), InBuf(0), OutBuf(0), OutSize(0), WriteVal(0), ReadVal(0), WriteBitCtr(0), ReadBitCtr(0) {}
DWORD GetOutSize() {return OutSize;}
void UseGIFEncoding(bool Use=true) {GIFEncoding=Use;} // When true, use marker codes: InitCode & EndCode.
protected:
DWORD OutSize;
bool GIFEncoding; // When true, use marker codes: InitCode & EndCode.
FILE* InFile; //May be used with files or memory:
FILE* OutFile;
const char* InBuf;
const char* InEnd; //Points at the Byte after the last Byte to read.
char* OutBuf;
bool GotStoredByte;
int StoredByteValue; // its an int so that we can test for EOF
bool EndData() {return GotStoredByte ? false : !(GotStoredByte=((StoredByteValue=GetC())!=EOF));}
BYTE ReadByte() {return (BYTE)(GotStoredByte ? GotStoredByte=false,StoredByteValue : GetC());}
int GetC() {if( InFile) return fgetc(InFile); else if(InBuf>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> CLZWStreamEncoder <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
class CLZWStreamEncoder : public CLZWStream {
public:
CLZWStreamEncoder() {}
CLZWStreamEncoder(CString& S) {
if(S.IsEmpty()) return;
CString D;
const char* Err=Encode(&*S,D.GetBufferSetLength(2*S.GetLength()),S.GetLength()+1); //+1 is to include the null terminator incase it really is a string
D.ReleaseBuffer(OutSize);
if(!Err && OutSize!=(DWORD)S.GetLength()) S=D; // Don't compress if an error occurred
}
CLZWStreamEncoder (const char* In, char* Out, DWORD Bytes) {Encode(In, Out, Bytes);}
const char* Encode(const char* In, char* Out, DWORD Bytes) {
InBuf=In;
InEnd=InBuf+Bytes;
OutBuf=Out;
const char* Return=Encode();
OutSize=OutBuf-Out;
if(OutSize>=Bytes) memcpy(Out,In,Bytes);
return Return;
}
CLZWStreamEncoder (const char* InPath, const char* OutPath) {Encode(InPath, OutPath);}
const char* Encode(const char* InPath, const char* OutPath) {
const char* Return=0;
if((InFile=fopen(InPath, "rb"))) {
if((OutFile=fopen(OutPath, "wb"))) {
Return=Encode();
if(ftell(InFile)<=ftell(OutFile)) {
OutFile=freopen(OutPath, "wb", OutFile); // You can't just rewind a stream thats been written to.
CopyFile(); //If we didn't compress things then just copy the file.
}
OutSize=ftell(OutFile);
fclose(OutFile);
}else Return=sBadFileName;
fclose(InFile);
}else Return=sBadFileName;
return Return;
}
private:
typedef struct DictValStruct {
WORD Character;
WORD Code;
DictValStruct* Left;
DictValStruct* Right;
} DictValNode, *DictValNodePtr;
/*
Returned parameters: None.
Action: Compresses with LZWStream method all bytes read by the function 'ReadInput'.
Errors: An input/output error could disturb the running of the program.
*/
const char* Encode() {
WriteByte(0xFF); //Compressed data token.
if(!EndInput()) {
MaxDictIdx=1<<12; //Range: 2^3 to 2^WordCounterBits.
BitsInCounter=8; //Range: 1 to WordCounterBits-1 (usually, for pictures, set up to 1, or 4, or 8, in the case of monochrome, or 16-colors, or 256-colors picture).
EncMinBitCtr=(BitsInCounter==1 ? 3 : BitsInCounter+1);
InitCode=1<<(EncMinBitCtr-1);
EndCode=InitCode+(GIFEncoding ? 1 : -1);
for(register WORD i=0; i<=EndCode; ++i) {
if(!(EncDict[i]=(DictValNodePtr)malloc(sizeof(DictValNode)))) {
while(i) free(EncDict[--i]);
return sBadMAlloc;
}
EncDict[i]->Character=i;
EncDict[i]->Code=i;
EncDict[i]->Left=0;
EncDict[i]->Right=0;
}
memset(&EncDict[i], 0, (MaxDictIdx-i)*sizeof(DictValNodePtr));
DictIdx=EndCode+1;
EncBitCtr=EncMinBitCtr;
if(GIFEncoding) WriteCodeLR(InitCode);
DictValNodePtr it=EncDict[ReadInput()];
while(!EndInput()) {
WORD Symbol=ReadInput();
DictValNodePtr NewNode=FindNode(it,Symbol);
if((NewNode!=it)&&(NewNode->Character==Symbol)) it=NewNode;
else {
WriteCodeLR(it->Code);
const char* Err=AddNode(it,NewNode,Symbol);
if(Err) return Err;
if(DictIdx==MaxDictIdx) {
if(GIFEncoding) WriteCodeLR(InitCode);
//Re-initialise EncDict
for(register WORD i=0; iLeft=EncDict[i]->Right=0;
DictIdx=EndCode+1;
EncBitCtr=EncMinBitCtr;
}
it=EncDict[Symbol];
} }
WriteCodeLR(it->Code);
if(GIFEncoding) WriteCodeLR(EndCode);
EncEndLR();
FreeDict();
}
return 0;
}
void FreeDict() {for(register WORD i=0; (i>ReadBitCtr);
ReadVal &= ((1<Character#Symbol).
The 'it' given to this routine must never be equal to 0.
*/
DictValNodePtr FindNode(DictValNodePtr it, WORD Symbol) {
DictValNodePtr NewNode;
if(it->Left==0) return it;
NewNode=it->Left;
while((NewNode->Character!=Symbol)&&(NewNode->Right)) NewNode=NewNode->Right;
return NewNode;
}
/*
Returned parameters: None.
Action: Creates a NewNode in the tree of EncDict.
The summoning of this routine is normally done after a call to to FindNode.
Errors: None.
*/
const char* AddNode(DictValNodePtr it, DictValNodePtr NewNode, WORD Symbol) {
DictValNodePtr pDictValNode=EncDict[DictIdx];
if(!pDictValNode) {
if(!(pDictValNode=EncDict[DictIdx]=(DictValNodePtr)malloc(sizeof(DictValNode)))) {
FreeDict();
return sBadMAlloc;
}
pDictValNode->Code=DictIdx;
pDictValNode->Left=0;
pDictValNode->Right=0;
}
pDictValNode->Character=Symbol;
if(it==NewNode) NewNode->Left=pDictValNode;
else NewNode->Right=pDictValNode;
if(++DictIdx==(WORD)(1<=8) {
WriteBitCtr-=8;
WriteByte((BYTE)(WriteVal >> WriteBitCtr));
WriteVal &= ((1<0) WriteByte((BYTE)(WriteVal << (8-WriteBitCtr)));
WriteVal=WriteBitCtr=0;
}
/*
Returned parameters: None.
Action: Sends the Value coded on 'EncBitCtr' bits in the stream of output codes.
The bits are stored from right to left. Example: aaabbbbcccc is written:
Bits 7 6 5 4 3 2 1 0
Byte 1 c b b b b a a a
Byte 2 ? ? ? ? ? c c c
Errors: An input/output error could disturb the running of the program.
*/
void WriteCodeRL(WORD Value) {
WriteVal |= ((DWORD)Value) << WriteBitCtr;
WriteBitCtr+=EncBitCtr;
while(WriteBitCtr>=8) {
WriteBitCtr-=8;
WriteByte((BYTE)(WriteVal&0xFF));
WriteVal=(WriteVal>>8) & ((1<0) WriteByte((BYTE)WriteVal);
WriteVal=WriteBitCtr=0;
}
};
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> CLZWStreamDecoder <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
class CLZWStreamDecoder : public CLZWStream {
public:
CLZWStreamDecoder() : AutoReInitDict(false) {}
CLZWStreamDecoder(CString& S) {
if(S.IsEmpty()) return;
CString D;
const char* Err=Decode(&*S, D.GetBufferSetLength(2*S.GetLength()), S.GetLength());
D.ReleaseBuffer();
S=Err ? "" : D; // On Error return an empty string
}
CLZWStreamDecoder (const char* In, char* Out, DWORD Bytes) {Decode(In, Out, Bytes);}
const char* Decode(const char* In, char* Out, DWORD Bytes) {
InBuf=In;
InEnd=InBuf+Bytes;
OutBuf=Out;
if(ReadByte()==0xFF) return Decode(); //Compressed data token.
memcpy(Out,In,Bytes);
return 0;
}
CLZWStreamDecoder (const char* InPath, const char* OutPath) {Decode(InPath, OutPath);}
const char* Decode(const char* InPath, const char* OutPath) {
const char* Return=0;
if((InFile=fopen(InPath, "rb"))) {
if((OutFile=fopen(OutPath, "wb"))) {
if(ReadByte()==0xFF) Return=Decode(); //Compressed data token.
else CopyFile(); //Its not compressed so just copy the file.
OutSize=ftell(OutFile);
fclose(OutFile);
}else Return=sBadFileName;
fclose(InFile);
}else Return=sBadFileName;
return Return;
}
private:
/*
If AutoReInitDict=true, the dictionary is always increased (even if this makes an overflow error!).
If AutoReInitDict=false, the dictionary is not updated when it reaches its maximal capacity.
The reception of the Code 'InitCode' will force the dictionary to by flushed.
AutoReInitDict is only considered when using GIFEncoding
*/
bool AutoReInitDict;
typedef struct DictLinkStruct {
WORD Character;
DictLinkStruct* predecessor;
} DictLinkNode, *pDictLinkNode;
void FreeDict() {for(register WORD i=0;(iCharacter=i;
DecDict[i]->predecessor=0;
}
DictIdx=EndCode+1;
DecBitCtr=DecMinBitCtr;
WORD PrevCode=0;
WORD FirstChar=0;
WORD CurCode=ReadCodeLR();
if(GIFEncoding) {
if(CurCode!=InitCode) {
FreeDict();
return sBadDataCode;
}
if((CurCode=ReadCodeLR())predecessor=0;
DictIdx=EndCode+1;
DecBitCtr=DecMinBitCtr;
CurCode=ReadCodeLR();
if(CurCodeDictIdx) return sBadDataCode;
if(AutoReInitDict) {
if(DictIdx==MaxDictIdx) return sDictOverflow;
FirstChar=WriteString(PrevCode,CurCode,FirstChar);
AddString(PrevCode,FirstChar);
PrevCode=CurCode;
CurCode=ReadCodeLR();
}else{
FirstChar=WriteString(PrevCode,CurCode,FirstChar);
if(DictIdx=DecBitCtr)) {
CurCode=ReadCodeLR();
if(CurCode>DictIdx) return sBadDataCode;
FirstChar=WriteString(PrevCode,CurCode,FirstChar);
if(DictIdx==MaxDictIdx-2) {
for(register WORD i=InitCode; (ipredecessor=0;
DictIdx=EndCode+1;
DecBitCtr=DecMinBitCtr;
if((!EndData())||(ReadBitCtr>=DecBitCtr)) {
PrevCode=(CurCode=ReadCodeLR());
FirstChar=WriteString(PrevCode,CurCode,FirstChar);
} } else AddString(PrevCode,FirstChar);
PrevCode=CurCode;
}
FreeDict();
}
EndOutput();
}
return 0;
}
/*
Returned parameters: None.
Action: Writes 'BitsOutCtr' via the function WriteByte.
Errors: An input/output error could disturb the running of the program.
*/
void WriteOutput(WORD Value) {
WriteVal=(WriteVal << BitsOutCtr) | Value;
WriteBitCtr+=BitsOutCtr;
while(WriteBitCtr>=8) {
WriteBitCtr-=8;
WriteByte((BYTE)(WriteVal >> WriteBitCtr));
WriteVal &= (1<0) WriteByte((BYTE)(WriteVal << (8-WriteBitCtr)));
WriteVal=WriteBitCtr=0;
}
/*
Returned parameters: 'Character' can have been modified.
Action: Sends the string in the output stream given by the 'link' of the LZWStream DecDict.
'Character' contains to the end of the routine the first Character of the string.
Errors: None (except a possible overflow of the operational stack allocated
by the program, which must allows at least 8*MaxDictIdx bytes, corresponding
to an exceptional case.
*/
void WriteLink(pDictLinkNode Link, WORD* Character) {
if(Link->predecessor) {
WriteLink(Link->predecessor,Character);
WriteOutput(Link->Character);
}else{
WriteOutput(Link->Character);
*Character=Link->Character;
} }
/*
Returned parameters: Returns a byte.
Action: Writes the string of bytes associated to 'it' and returns
the first Character of this string.
Errors: None.
*/
WORD WriteString(WORD PrevCode, WORD CurCode, WORD FirstChar) {
WORD Character;
if(CurCodeCharacter=FirstChar;
DecDict[DictIdx]->predecessor=DecDict[Code];
if(++DictIdx+1==(WORD)(1<>ReadBitCtr);
ReadVal &= ((1<>= DecBitCtr;
return ReadCode;
}
};
#endif //ndef LZWStreamh