slidescript/src/compression.c

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;
}
*/