251 lines
7.6 KiB
C
251 lines
7.6 KiB
C
#include <stdio.h>
|
||
#include <stdint.h>
|
||
#include <string.h>
|
||
#include <stdlib.h>
|
||
|
||
#define WINDOW_SIZE 4096
|
||
#define LOOKAHEAD_BUFFER_SIZE 18
|
||
|
||
typedef struct {
|
||
uint16_t offset;
|
||
uint8_t length;
|
||
uint8_t next_char;
|
||
} LZ77Triple;
|
||
|
||
int lz77_compress(const char *input_filename, const char *output_filename) {
|
||
// Open the input file in binary read mode
|
||
FILE *input_file = fopen(input_filename, "rb");
|
||
if (!input_file) {
|
||
perror("Error opening input file");
|
||
return -1;
|
||
}
|
||
|
||
// Open the output file in binary write mode
|
||
FILE *output_file = fopen(output_filename, "wb");
|
||
if (!output_file) {
|
||
perror("Error opening output file");
|
||
fclose(input_file);
|
||
return -2;
|
||
}
|
||
|
||
uint8_t *window = (uint8_t *)malloc(WINDOW_SIZE + LOOKAHEAD_BUFFER_SIZE);
|
||
if (!window) {
|
||
perror("Memory allocation failed");
|
||
fclose(input_file);
|
||
fclose(output_file);
|
||
return -3;
|
||
}
|
||
|
||
size_t window_start = 0;
|
||
size_t lookahead_start = 0;
|
||
size_t bytes_read;
|
||
|
||
// Initialize the window with data from the input file
|
||
bytes_read = fread(window + WINDOW_SIZE, 1, LOOKAHEAD_BUFFER_SIZE, input_file);
|
||
|
||
while (bytes_read > 0) {
|
||
size_t best_match_offset = 0;
|
||
size_t best_match_length = 0;
|
||
|
||
// Search for the best match within the sliding window
|
||
for (size_t i = window_start; i < WINDOW_SIZE + lookahead_start; i++) {
|
||
size_t match_length = 0;
|
||
|
||
while (match_length < bytes_read &&
|
||
window[i + match_length] == window[WINDOW_SIZE + match_length]) {
|
||
match_length++;
|
||
if (match_length >= LOOKAHEAD_BUFFER_SIZE) {
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (match_length > best_match_length) {
|
||
best_match_length = match_length;
|
||
best_match_offset = WINDOW_SIZE + lookahead_start - i;
|
||
}
|
||
}
|
||
|
||
// Create a triple and write it to the output file
|
||
LZ77Triple triple;
|
||
triple.offset = (uint16_t)best_match_offset;
|
||
triple.length = (uint8_t)best_match_length;
|
||
triple.next_char = window[WINDOW_SIZE + best_match_length];
|
||
|
||
// Write the triple to the output file
|
||
fwrite(&triple, sizeof(LZ77Triple), 1, output_file);
|
||
|
||
// Slide the window
|
||
window_start = (window_start + best_match_length + 1) % WINDOW_SIZE;
|
||
lookahead_start = (lookahead_start + best_match_length + 1) % LOOKAHEAD_BUFFER_SIZE;
|
||
|
||
// Read new byte into the lookahead buffer
|
||
bytes_read = fread(window + WINDOW_SIZE, 1, LOOKAHEAD_BUFFER_SIZE - lookahead_start, input_file);
|
||
}
|
||
|
||
// Cleanup and close files
|
||
fclose(input_file);
|
||
fclose(output_file);
|
||
free(window);
|
||
|
||
return 0;
|
||
}
|
||
|
||
int lz77_decompress(const char *input_filename, const char *output_filename) {
|
||
FILE *input = fopen(input_filename, "rb");
|
||
FILE *output = fopen(output_filename, "wb");
|
||
if (!input || !output) {
|
||
if (input) fclose(input);
|
||
if (output) fclose(output);
|
||
return -1; // Помилка відкриття файлу
|
||
}
|
||
|
||
size_t buffer_size = 4096; // Максимальний розмір вікна
|
||
unsigned char *window = malloc(buffer_size);
|
||
size_t window_size = 0; // Розмір заповненої частини вікна
|
||
size_t window_pos = 0; // Поточна позиція в межах вікна
|
||
|
||
if (!window) {
|
||
fclose(input);
|
||
fclose(output);
|
||
return -1; // Помилка пам'яті
|
||
}
|
||
|
||
while (!feof(input)) {
|
||
unsigned char flag;
|
||
if (fread(&flag, 1, 1, input) != 1) break;
|
||
|
||
for (int i = 0; i < 8 && !feof(input); i++) {
|
||
if (flag & (1 << i)) { // Літеральний символ
|
||
unsigned char literal;
|
||
if (fread(&literal, 1, 1, input) != 1) break;
|
||
|
||
fputc(literal, output);
|
||
|
||
// Додати символ у вікно
|
||
window[window_pos] = literal;
|
||
window_pos = (window_pos + 1) % buffer_size;
|
||
if (window_size < buffer_size) window_size++;
|
||
} else { // Посилання
|
||
unsigned short offset_length;
|
||
if (fread(&offset_length, 2, 1, input) != 1) break;
|
||
|
||
size_t offset = offset_length >> 4;
|
||
size_t length = (offset_length & 0xF) + 3;
|
||
|
||
for (size_t j = 0; j < length; j++) {
|
||
unsigned char byte = window[(window_pos - offset + buffer_size) % buffer_size];
|
||
fputc(byte, output);
|
||
|
||
// Додати байт у вікно
|
||
window[window_pos] = byte;
|
||
window_pos = (window_pos + 1) % buffer_size;
|
||
if (window_size < buffer_size) window_size++;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
free(window);
|
||
fclose(input);
|
||
fclose(output);
|
||
return 0; // Успішна декомпресія
|
||
}
|
||
|
||
|
||
|
||
|
||
void rle_compress(const char *input_filename, const char *output_filename) {
|
||
FILE *input_file = fopen(input_filename, "rb");
|
||
if (!input_file) {
|
||
perror("Error opening input file");
|
||
return;
|
||
}
|
||
|
||
FILE *output_file = fopen(output_filename, "wb");
|
||
if (!output_file) {
|
||
perror("Error opening output file");
|
||
fclose(input_file);
|
||
return;
|
||
}
|
||
|
||
uint8_t current_byte, previous_byte;
|
||
uint8_t count = 1;
|
||
|
||
if (fread(&previous_byte, 1, 1, input_file) != 1) {
|
||
printf("Input file is empty or read error occurred.\n");
|
||
fclose(input_file);
|
||
fclose(output_file);
|
||
return;
|
||
}
|
||
|
||
while (fread(¤t_byte, 1, 1, input_file) == 1) {
|
||
if (current_byte == previous_byte && count < 255) {
|
||
count++;
|
||
} else {
|
||
fwrite(&previous_byte, 1, 1, output_file);
|
||
fwrite(&count, 1, 1, output_file);
|
||
printf("Writing byte: %c with count: %d\n", previous_byte, count);
|
||
previous_byte = current_byte;
|
||
count = 1;
|
||
}
|
||
}
|
||
|
||
fwrite(&previous_byte, 1, 1, output_file);
|
||
fwrite(&count, 1, 1, output_file);
|
||
printf("Writing byte: %c with count: %d\n", previous_byte, count);
|
||
|
||
fclose(input_file);
|
||
fclose(output_file);
|
||
}
|
||
|
||
|
||
|
||
int rle_decompress(const char *input_filename, const char *output_filename) {
|
||
// Open the input file in binary read mode
|
||
FILE *input_file = fopen(input_filename, "rb");
|
||
if (!input_file) {
|
||
perror("Error opening input file");
|
||
return -1;
|
||
}
|
||
|
||
// Open the output file in binary write mode
|
||
FILE *output_file = fopen(output_filename, "wb");
|
||
if (!output_file) {
|
||
perror("Error opening output file");
|
||
fclose(input_file);
|
||
return -2;
|
||
}
|
||
|
||
uint8_t byte;
|
||
uint8_t count;
|
||
size_t decompressed_size = 0;
|
||
|
||
// Read [byte, count] pairs from the input file
|
||
while (fread(&byte, 1, 1, input_file) == 1) {
|
||
if (fread(&count, 1, 1, input_file) != 1) {
|
||
// Handle malformed input file
|
||
fprintf(stderr, "Error: Malformed input file\n");
|
||
fclose(input_file);
|
||
fclose(output_file);
|
||
return -3;
|
||
}
|
||
|
||
// Write 'count' occurrences of 'byte' to the output file
|
||
for (uint8_t i = 0; i < count; i++) {
|
||
if (fwrite(&byte, 1, 1, output_file) != 1) {
|
||
perror("Error writing to output file");
|
||
fclose(input_file);
|
||
fclose(output_file);
|
||
return -4;
|
||
}
|
||
decompressed_size++;
|
||
}
|
||
}
|
||
|
||
// Clean up and close files
|
||
fclose(input_file);
|
||
fclose(output_file);
|
||
|
||
return (int)decompressed_size;
|
||
}
|