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