slidescript/src/compression.c

231 lines
6.8 KiB
C

// 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 "deps.h"
#include "compression.h"
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, size_t malloc_size, uint8_t pointer_length_width)
{
FILE *in, *out;
uint8_t *uncompressed_text, *compressed_text;
uint32_t uncompressed_size, compressed_size;
in = fopen(filename_in, "r");
if(in == NULL)
return 0;
uncompressed_size = fsize(in);
uncompressed_text = malloc(uncompressed_size);
if((uncompressed_size != fread(uncompressed_text, 1, uncompressed_size, in)))
{
free(uncompressed_text);
return 0;
}
fclose(in);
compressed_text = malloc(malloc_size);
compressed_size = lz77_compress(uncompressed_text, uncompressed_size, compressed_text, pointer_length_width);
out = fopen(filename_out, "w");
if(out == NULL)
{
free(uncompressed_text);
free(compressed_text);
return 0;
}
if((compressed_size != fwrite(compressed_text, 1, compressed_size, out)))
{
free(uncompressed_text);
free(compressed_text);
fclose(out);
return 0;
}
fclose(out);
free(compressed_text);
free(uncompressed_text);
return compressed_size;
}
uint32_t ss_decompress (char *filename_in, char *filename_out)
{
FILE *in, *out;
uint32_t compressed_size, uncompressed_size;
uint8_t *compressed_text, *uncompressed_text;
in = fopen(filename_in, "r");
if(in == NULL)
{
return 0;
}
compressed_size = fsize(in);
compressed_text = malloc(compressed_size);
if(fread(compressed_text, 1, compressed_size, in) != compressed_size)
{
free(compressed_text);
return 0;
}
fclose(in);
uncompressed_size = *((uint32_t *) compressed_text);
uncompressed_text = malloc(uncompressed_size);
if(lz77_decompress(compressed_text, uncompressed_text) != uncompressed_size)
{
free(compressed_text);
free(uncompressed_text);
return 0;
}
out = fopen(filename_out, "w");
if(out == NULL)
{
free(compressed_text);
free(uncompressed_text);
return 0;
}
if(fwrite(uncompressed_text, 1, uncompressed_size, out) != uncompressed_size)
{
free(compressed_text);
free(uncompressed_text);
fclose(out);
return 0;
}
fclose(out);
free(compressed_text);
free(uncompressed_text);
return uncompressed_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;
}
*/