#include <iostream>
#include <fstream>
#include <cstdlib>
#include <iomanip>
const uint32_t DATA_SIZE = 65535;
const uint32_t WINDOW_SIZE = 4095;
const uint32_t MAX_MATCH_LENGTH = 17;
const uint32_t MIN_MATCH_LENGTH = 2;
const uint8_t READ_BIT = 0x01;
const uint8_t WRITE_BIT = 0x80;
struct chunk_t
{
uint8_t code, bitcount;
};
struct lz_t
{
uint16_t length, offset, distance;
};
lz_t search_lz (uint32_t pos, uint8_t* buffer, uint32_t inputsize);
uint32_t write_lz (uint32_t &read_pos, uint8_t* write_buf, uint32_t &write_pos, lz_t lz, chunk_t &chunk);
uint32_t write_lit (uint32_t &read_pos, uint8_t* read_buf, uint32_t &write_pos, uint8_t* write_buf, chunk_t &chunk);
void reset_chunk (chunk_t &chunk);
using namespace std;
int main(int argc, char **argv)
{
uint8_t* _inputData;
uint8_t* _outputData;
uint32_t _inputPosition;
uint32_t _outputPosition;
uint32_t _headerPosition;
uint32_t _inputSize;
uint32_t _outputSize;
uint8_t h1 = 0, h2 = 0;
lz_t lz, lz_alt;
chunk_t chunk;
chunk.code=0;
chunk.bitcount=0;
_headerPosition = 2;
_inputPosition = 0;
_outputPosition = 3;
const char* input_name = "st101.uncompress";
const char* output_name = "st101_compressed.bin";
FILE *input_file;
input_file = fopen(input_name, "rb");
if (input_file==NULL)
{
fputs ("Input file error",stderr);
exit (1);
}
fseek(input_file, 0, SEEK_END);
_inputSize=ftell(input_file);
_outputSize=0;
cout << hex << "Size of data to be compressed: " << (int)_inputSize;
rewind(input_file);
_inputData = (uint8_t*) malloc (sizeof(uint8_t)*_inputSize);
_outputData = (uint8_t*) malloc (sizeof(uint8_t)*_inputSize);
fread(_inputData,sizeof(uint8_t),_inputSize, input_file);
fclose(input_file);
cout << hex << "\nStarting compression...";
cout << hex << "\nInput position = [" << setw(4) << (int) _inputPosition << "]";
cout << hex << "\tOutput position = [" << setw(4) << (int) _outputPosition << "]";
write_lit(_inputPosition, _inputData, _outputPosition, _outputData, chunk);
while (_inputPosition<_inputSize)
{
cout << hex << "\nInput position = [" << setw(4) << (int) _inputPosition << "]";
cout << hex << "\tOutput position = [" << setw(4) << (int) _outputPosition << "]";
lz=search_lz(_inputPosition,_inputData,_inputSize);
lz_alt=search_lz(_inputPosition+1,_inputData,_inputSize);
if (lz_alt.length>lz.length && lz_alt.length>=MIN_MATCH_LENGTH)
{
write_lit(_inputPosition, _inputData, _outputPosition, _outputData, chunk);
}
else
{
if (lz.length<MIN_MATCH_LENGTH)
{
write_lit(_inputPosition, _inputData, _outputPosition, _outputData, chunk);
}
else
{
write_lz(_inputPosition, _outputData, _outputPosition, lz, chunk);
}
}
if (chunk.bitcount==8)
{
_outputData[_headerPosition]=chunk.code;
chunk.code=0x00;
chunk.bitcount=0;
_headerPosition=_outputPosition++;
}
}
if (chunk.bitcount<8 && chunk.bitcount!=0)
{
int bit_shift;
bit_shift=8-chunk.bitcount;
chunk.code=chunk.code<<bit_shift;
_outputData[_headerPosition]=chunk.code;
}
h1=(_outputPosition-2)&0xff;
h2=((_outputPosition-2)&0xff00)>>8;
//h1=(_inputSize)&0xff;
//h2=((_inputSize)&0xff00)>>8;
_outputData[0]=h1;
_outputData[1]=h2;
_outputSize = _outputPosition;
cout << hex << "\nSize of compressed data: " << (int)(_outputPosition) << endl ;
FILE *output_file;
if (!_outputData)
{
return 1;
}
output_file = fopen(output_name, "wb");
if (output_file==NULL)
{
fputs ("Output file error",stderr);
exit (1);
}
if(!output_file)
{
return 1;
}
fwrite(_outputData, _outputSize, 1, output_file);
fclose(output_file);
free(_outputData);
free(_inputData);
return 0;
}
lz_t search_lz (uint32_t pos, uint8_t* buffer, uint32_t inputsize)
{
lz_t variant;
variant.length=0;
variant.offset=0;
variant.distance=0;
uint16_t lz_off=0;
uint16_t lz_len=0;
uint8_t match=0;
int win;
win=pos-(WINDOW_SIZE);
if (win<0) win=0;
for (int i=win; i<pos && i+MAX_MATCH_LENGTH < pos+MAX_MATCH_LENGTH; i++)
{
if ((buffer[i]==buffer[pos]))
{
match=1;
while((buffer[i+match]==buffer[pos+match])&&(pos+match<inputsize))
{
if (match>=MAX_MATCH_LENGTH)
{
break;
}
match++;
}
if(match>lz_len)
{
lz_len=match;
lz_off=i;
}
}
if(match>=MAX_MATCH_LENGTH)
{
lz_len=MAX_MATCH_LENGTH;
break;
}
}
variant.length=lz_len;
variant.offset=lz_off;
variant.distance=pos-lz_off;
return variant;
}
uint32_t write_lz(uint32_t &read_pos, uint8_t* write_buf, uint32_t &write_pos, lz_t lz, chunk_t &chunk)
{
uint32_t lz_size;
uint8_t lz1,lz2;
uint16_t lz_dist, lz_len;
uint16_t lz_d1, lz_d2;
lz_dist=lz.distance;
lz_len=(lz.length-MIN_MATCH_LENGTH)&0x0f;
lz_len=(lz_len<<4)&0xf0;
lz_d1=lz_dist&0xf00;
lz_d2=lz_dist&0xff;
lz1=lz_d2;
lz2=lz_d1+lz_len;
write_buf[write_pos++]=lz1;
write_buf[write_pos++]=lz2;
read_pos=read_pos+lz.length;
cout << hex << "\nLZ = ["<< setw(2) << (int)lz1 << (int)lz2 << "]";
cout << hex << "\tLENGTH = [" << setw(2) << (int)lz.length << "]";
cout << hex << "\tOFFSET = [" << setw(2) << (int)lz.offset << "]";
cout << hex << "\tDISTANCE = [" << setw(2) << (int)lz.distance << "]";
chunk.code=chunk.code>>1;
chunk.code=chunk.code|0x00;
chunk.bitcount++;
lz_size=2;
return lz_size;
};
uint32_t write_lit(uint32_t &read_pos, uint8_t* read_buf, uint32_t &write_pos, uint8_t* write_buf, chunk_t &chunk)
{
int lit_size;
cout << hex << "\nLITERAL = ["<< setw(2) << (int)read_buf[read_pos] << "]";
write_buf[write_pos++]=read_buf[read_pos++];
chunk.code=chunk.code>>1;
chunk.code=chunk.code|0x80;
chunk.bitcount++;
lit_size=1;
return lit_size;
}
void reset_chunk (chunk_t &chunk)
{
chunk.code=0;
chunk.bitcount=0;
}