341 lines
10 KiB
C
Executable File
341 lines
10 KiB
C
Executable File
// LZ77 compression examples, simple and lightweight
|
|
// Being quick to process and execute, this will be great
|
|
// For internal compression on modern machines
|
|
//
|
|
// Andy Herbert
|
|
// lz1 https://github.com/andyherbert/lz1
|
|
//
|
|
|
|
#include "inc/deps.h"
|
|
#include "inc/compression.h"
|
|
|
|
//--------------------------------------------------------------------
|
|
|
|
#define MY_ERR "Error: lz77"
|
|
|
|
#define my_free(dm) \
|
|
{ \
|
|
if (dm == (void *) 0) \
|
|
{ fprintf (stderr, "WTH. Mooo.\n"); exit (1); }; \
|
|
free (dm); \
|
|
dm = (void *) 0; \
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
|
|
uint32_t lz77_compress (uint8_t *uncompressed_text, uint32_t uncompressed_size, uint8_t *compressed_text, uint8_t pointer_length_width)
|
|
{
|
|
uint16_t pointer_pos, temp_pointer_pos, output_pointer, pointer_length, temp_pointer_length;
|
|
uint32_t compressed_pointer, output_size, coding_pos, output_lookahead_ref, look_behind, look_ahead;
|
|
uint16_t pointer_pos_max, pointer_length_max;
|
|
pointer_pos_max = pow(2, 16 - pointer_length_width);
|
|
pointer_length_max = pow(2, pointer_length_width);
|
|
|
|
*((uint32_t *) compressed_text) = uncompressed_size;
|
|
*(compressed_text + 4) = pointer_length_width;
|
|
compressed_pointer = output_size = 5;
|
|
|
|
for(coding_pos = 0; coding_pos < uncompressed_size; ++coding_pos)
|
|
{
|
|
pointer_pos = 0;
|
|
pointer_length = 0;
|
|
for(temp_pointer_pos = 1; (temp_pointer_pos < pointer_pos_max) && (temp_pointer_pos <= coding_pos); ++temp_pointer_pos)
|
|
{
|
|
look_behind = coding_pos - temp_pointer_pos;
|
|
look_ahead = coding_pos;
|
|
for(temp_pointer_length = 0; uncompressed_text[look_ahead++] == uncompressed_text[look_behind++]; ++temp_pointer_length)
|
|
{
|
|
if(temp_pointer_length == pointer_length_max)
|
|
break;
|
|
}
|
|
|
|
if(temp_pointer_length > pointer_length)
|
|
{
|
|
pointer_pos = temp_pointer_pos;
|
|
pointer_length = temp_pointer_length;
|
|
if(pointer_length == pointer_length_max)
|
|
break;
|
|
}
|
|
}
|
|
coding_pos += pointer_length;
|
|
if((coding_pos == uncompressed_size) && pointer_length)
|
|
{
|
|
output_pointer = (pointer_length == 1) ? 0 : ((pointer_pos << pointer_length_width) | (pointer_length - 2));
|
|
output_lookahead_ref = coding_pos - 1;
|
|
}
|
|
else
|
|
{
|
|
output_pointer = (pointer_pos << pointer_length_width) | (pointer_length ? (pointer_length - 1) : 0);
|
|
output_lookahead_ref = coding_pos;
|
|
}
|
|
*((uint16_t *) (compressed_text + compressed_pointer)) = output_pointer;
|
|
compressed_pointer += 2;
|
|
*(compressed_text + compressed_pointer++) = *(uncompressed_text + output_lookahead_ref);
|
|
output_size += 3;
|
|
}
|
|
|
|
return output_size;
|
|
}
|
|
|
|
uint32_t lz77_decompress (uint8_t *compressed_text, uint8_t *uncompressed_text)
|
|
{
|
|
uint8_t pointer_length_width;
|
|
uint16_t input_pointer, pointer_length, pointer_pos, pointer_length_mask;
|
|
uint32_t compressed_pointer, coding_pos, pointer_offset, uncompressed_size;
|
|
|
|
uncompressed_size = *((uint32_t *) compressed_text);
|
|
pointer_length_width = *(compressed_text + 4);
|
|
compressed_pointer = 5;
|
|
|
|
pointer_length_mask = pow(2, pointer_length_width) - 1;
|
|
|
|
for(coding_pos = 0; coding_pos < uncompressed_size; ++coding_pos)
|
|
{
|
|
input_pointer = *((uint16_t *) (compressed_text + compressed_pointer));
|
|
compressed_pointer += 2;
|
|
pointer_pos = input_pointer >> pointer_length_width;
|
|
pointer_length = pointer_pos ? ((input_pointer & pointer_length_mask) + 1) : 0;
|
|
if(pointer_pos)
|
|
for(pointer_offset = coding_pos - pointer_pos; pointer_length > 0; --pointer_length)
|
|
uncompressed_text[coding_pos++] = uncompressed_text[pointer_offset++];
|
|
*(uncompressed_text + coding_pos) = *(compressed_text + compressed_pointer++);
|
|
}
|
|
|
|
return coding_pos;
|
|
}
|
|
|
|
long fsize (FILE *in)
|
|
{
|
|
long pos, length;
|
|
pos = ftell(in);
|
|
fseek(in, 0L, SEEK_END);
|
|
length = ftell(in);
|
|
fseek(in, pos, SEEK_SET);
|
|
return length;
|
|
}
|
|
|
|
uint32_t ss_compress (const char *filename_in, char *filename_out, uint8_t pointer_length_width)
|
|
{
|
|
FILE *in, *out;
|
|
uint8_t *uncompressed_text, *compressed_text;
|
|
uint32_t uncompressed_size, compressed_size;
|
|
|
|
in = fopen(filename_in, "rb");
|
|
if(in == NULL)
|
|
return 0;
|
|
uncompressed_size = fsize(in);
|
|
uncompressed_text = malloc(uncompressed_size + 20);
|
|
// +20 for uncompressed data size sway in algorithm
|
|
if((uncompressed_size != fread(uncompressed_text, 1, uncompressed_size, in)))
|
|
{
|
|
my_free(uncompressed_text);
|
|
return 0;
|
|
}
|
|
fclose(in);
|
|
|
|
compressed_text = malloc((int)(uncompressed_size * 1.25));
|
|
// * 2 for uncompressed size climb on first compress pass
|
|
compressed_size = lz77_compress(uncompressed_text, uncompressed_size, compressed_text, pointer_length_width);
|
|
|
|
out = fopen(filename_out, "wb");
|
|
if(out == NULL)
|
|
{
|
|
my_free(uncompressed_text);
|
|
my_free(compressed_text);
|
|
return 0;
|
|
}
|
|
|
|
if((compressed_size != fwrite(compressed_text, 1, compressed_size, out)))
|
|
{
|
|
my_free(uncompressed_text);
|
|
my_free(compressed_text);
|
|
fclose(out);
|
|
return 0;
|
|
}
|
|
fclose(out);
|
|
|
|
free(compressed_text);
|
|
free(uncompressed_text);
|
|
|
|
return compressed_size;
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
|
|
#ifndef ZERO
|
|
#define ZERO 0
|
|
#endif
|
|
|
|
#ifndef ONE
|
|
#define ONE 1
|
|
#endif
|
|
|
|
//--------------------------------------------------------------------
|
|
|
|
uint32_t ss_decompress
|
|
(
|
|
char *ifname, // Input -file name
|
|
char *ofname // Output -file name
|
|
)
|
|
{
|
|
FILE *ifp; // Input -file pointer
|
|
FILE *ofp; // Output -file pointer
|
|
|
|
uint8_t *dm_comp; // DM: Compressed data
|
|
uint8_t *dm_deco; // DM: Uncompressed data
|
|
|
|
int orig_size; // Original size
|
|
int comp_size; // Compressed size
|
|
int deco_size; // Decompressed size
|
|
int writ_size; // Written size
|
|
|
|
//--------------------------------------------------------------------
|
|
// Open input file.
|
|
|
|
if ((ifp = fopen (ifname, "rb")) == NULL)
|
|
{ // Error
|
|
fprintf (stderr,
|
|
"%s: File not found: %s\n",
|
|
MY_ERR, ifname);
|
|
|
|
return ZERO;
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
// Set up compressed-data buffer.
|
|
|
|
// Compressed size
|
|
comp_size = (int) fsize (ifp);
|
|
// DM: Compressed data
|
|
dm_comp = (uint8_t *) malloc (comp_size + 10);
|
|
// Add some extra memory at the end of malloc comp_size
|
|
// Saves dirty archive size sway, WIP
|
|
if (dm_comp == NULL) // Error?
|
|
{ // Yes - Error exit
|
|
fprintf (stderr,
|
|
"%s: Error: malloc failed\n",
|
|
MY_ERR);
|
|
|
|
return ZERO;
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
// Read compressed data.
|
|
|
|
if (fread (dm_comp, ONE, comp_size, ifp) != (size_t) comp_size)
|
|
{ // Error
|
|
my_free (dm_comp); // Release DM
|
|
|
|
fprintf (stderr,
|
|
"%s: Read of input data failed\n",
|
|
MY_ERR);
|
|
|
|
return ZERO;
|
|
}
|
|
|
|
fclose (ifp); // Close input file
|
|
|
|
//--------------------------------------------------------------------
|
|
// Set up decompressed-data buffer.
|
|
|
|
orig_size = (int) *((uint32_t *) dm_comp);
|
|
dm_deco = (uint8_t *) malloc (orig_size + 20);
|
|
// +20 to cover byte sway, dirty trick for mem leak
|
|
if (dm_comp == NULL) // Error?
|
|
{ // Yes
|
|
my_free (dm_comp); // Release DM
|
|
|
|
fprintf (stderr, "%s: malloc failed\n", MY_ERR);
|
|
return ZERO;
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
// Decompress.
|
|
|
|
deco_size = (int) lz77_decompress (dm_comp, dm_deco);
|
|
my_free (dm_comp); // Release DM
|
|
|
|
if (deco_size < orig_size) // Error?
|
|
{ // Yes
|
|
my_free (dm_deco); // Release DM
|
|
|
|
fprintf (stderr,
|
|
"%s: deco size %d < orig size %d\n",
|
|
MY_ERR, deco_size, orig_size);
|
|
|
|
return ZERO;
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
// Open output file.
|
|
|
|
if ((ofp = fopen (ofname, "wb")) == NULL)
|
|
{ // Error
|
|
my_free (dm_deco); // Release DM
|
|
|
|
fprintf (stderr,
|
|
"%s: Can't open output file: %s\n",
|
|
MY_ERR, ofname);
|
|
|
|
return ZERO;
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
// Write to output file.
|
|
|
|
writ_size = (int) fwrite (dm_deco, ONE, deco_size, ofp);
|
|
|
|
fclose (ofp); // Close output file
|
|
my_free (dm_deco); // Release DM
|
|
|
|
if (writ_size != deco_size) // Error?
|
|
{ // Yes
|
|
fprintf (stderr,
|
|
"%s: Bytes written %d != Data size %d\n",
|
|
MY_ERR, writ_size, deco_size);
|
|
|
|
return ZERO;
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
// Wrap it up.
|
|
|
|
return deco_size; // == orig_size
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
|
|
/*
|
|
int main (int argc, char const *argv[])
|
|
{
|
|
FILE *in;
|
|
|
|
char filename[129];
|
|
char filedecout[141];
|
|
|
|
if(argc < 2)
|
|
{
|
|
printf("Please enter a filename: ./comp file.txt");
|
|
}
|
|
|
|
in = fopen(argv[1], "r");
|
|
if(in == NULL)
|
|
return 0;
|
|
|
|
if(strlen(argv[1]) > 128)
|
|
{
|
|
printf("Filename too long");
|
|
return 1;
|
|
}
|
|
|
|
sprintf(filename, "%s.ss", argv[1]);
|
|
sprintf(filedecout, "%s.1", argv[1]);
|
|
|
|
printf("Original size: %ld\n", fsize(in));
|
|
fclose(in);
|
|
for(uint8_t i = 1; i <= 6; ++i)
|
|
printf("Compressed (%i): %u, decompressed: (%u)\n", i, ss_compress(argv[1], filename, 20000000, i), ss_decompress(filename, filedecout));
|
|
return 0;
|
|
}
|
|
*/
|