usaa24/sk1/compressor.c

378 lines
14 KiB
C
Raw Permalink Normal View History

2025-01-31 16:51:34 +00:00
#include "compressor.h" // Hlavičkový súbor, ktorý obsahuje deklarácie funkcií a štruktúr používaných na kompresiu a dekompresiu.
#include <stdio.h> // Knižnica pre prácu so vstupom a výstupom.
#include <stdlib.h> // Knižnica pre alokáciu pamäte a ďalšie základné funkcie.
#include <string.h> // Knižnica pre prácu s reťazcami.
2025-01-19 18:46:52 +00:00
2025-01-31 16:51:34 +00:00
// Funkcia na čítanie obsahu súboru do pamäte.
2025-01-19 18:46:52 +00:00
unsigned char* read_file(const char* file_name, size_t* size) {
2025-01-31 16:51:34 +00:00
FILE* file = fopen(file_name, "rb"); // Otvorenie súboru v režime čítania binárnych dát.
2025-01-19 18:46:52 +00:00
if (!file) {
2025-01-31 16:51:34 +00:00
perror("Error opening file"); // Chybové hlásenie, ak sa súbor nedá otvoriť.
2025-01-19 18:46:52 +00:00
return NULL;
}
2025-01-31 16:51:34 +00:00
fseek(file, 0, SEEK_END); // Posun ukazovateľa na koniec súboru.
*size = ftell(file); // Zistenie veľkosti súboru.
rewind(file); // Návrat ukazovateľa na začiatok súboru.
2025-01-19 18:46:52 +00:00
2025-01-31 16:51:34 +00:00
unsigned char* buffer = (unsigned char*)malloc(*size); // Alokácia pamäte pre obsah súboru.
2025-01-19 18:46:52 +00:00
if (!buffer) {
2025-01-31 16:51:34 +00:00
perror("Memory allocation error"); // Chybové hlásenie, ak sa nepodarí alokovať pamäť.
2025-01-19 18:46:52 +00:00
fclose(file);
return NULL;
}
2025-01-31 16:51:34 +00:00
fread(buffer, 1, *size, file); // Načítanie obsahu súboru do bufferu.
2025-01-19 18:46:52 +00:00
fclose(file);
2025-01-31 16:51:34 +00:00
return buffer; // Návrat ukazovateľa na buffer.
2025-01-19 18:46:52 +00:00
}
2025-01-31 16:51:34 +00:00
// Funkcia na zápis obsahu z pamäte do súboru.
2025-01-19 18:46:52 +00:00
int write_file(const char* file_name, const unsigned char* buffer, size_t size) {
2025-01-31 16:51:34 +00:00
FILE* file = fopen(file_name, "wb"); // Otvorenie súboru v režime zápisu binárnych dát.
2025-01-19 18:46:52 +00:00
if (!file) {
perror("Error opening file");
return -1;
}
2025-01-31 16:51:34 +00:00
fwrite(buffer, 1, size, file); // Zápis dát do súboru.
2025-01-19 18:46:52 +00:00
fclose(file);
return 0;
}
2025-01-31 16:51:34 +00:00
// Funkcia na vytvorenie Huffmanovho stromu z daných dát.
2025-01-19 18:46:52 +00:00
HuffmanNode* build_huffman_tree(const unsigned char* data, size_t size) {
2025-01-31 16:51:34 +00:00
size_t frequencies[256] = {0}; // Pole na počítanie frekvencie každého znaku.
2025-01-19 18:46:52 +00:00
for (size_t i = 0; i < size; i++) {
2025-01-31 16:51:34 +00:00
frequencies[data[i]]++; // Zvýšenie frekvencie pre daný znak.
2025-01-19 18:46:52 +00:00
}
2025-01-31 16:51:34 +00:00
HuffmanNode* nodes[256]; // Pole uzlov Huffmanovho stromu.
2025-01-19 18:46:52 +00:00
int node_count = 0;
2025-01-31 16:51:34 +00:00
// Vytvorenie listových uzlov pre znaky s nenulovou frekvenciou.
2025-01-19 18:46:52 +00:00
for (int i = 0; i < 256; i++) {
if (frequencies[i] > 0) {
HuffmanNode* node = (HuffmanNode*)malloc(sizeof(HuffmanNode));
2025-01-31 16:51:34 +00:00
node->symbol = (unsigned char)i; // Znak reprezentovaný uzlom.
node->frequency = frequencies[i]; // Frekvencia znaku.
2025-01-19 18:46:52 +00:00
node->left = NULL;
node->right = NULL;
2025-01-31 16:51:34 +00:00
nodes[node_count++] = node; // Pridanie uzla do zoznamu.
2025-01-19 18:46:52 +00:00
}
}
2025-01-31 16:51:34 +00:00
// Opakované zlúčenie dvoch uzlov s najnižšou frekvenciou.
2025-01-19 18:46:52 +00:00
while (node_count > 1) {
2025-01-31 16:51:34 +00:00
// Vyhľadanie dvoch uzlov s najnižšou frekvenciou.
2025-01-19 18:46:52 +00:00
int min1 = 0, min2 = 1;
if (nodes[min2]->frequency < nodes[min1]->frequency) {
min1 = 1;
min2 = 0;
}
for (int i = 2; i < node_count; i++) {
if (nodes[i]->frequency < nodes[min1]->frequency) {
min2 = min1;
min1 = i;
} else if (nodes[i]->frequency < nodes[min2]->frequency) {
min2 = i;
}
}
2025-01-31 16:51:34 +00:00
// Vytvorenie nového rodičovského uzla.
2025-01-19 18:46:52 +00:00
HuffmanNode* new_node = (HuffmanNode*)malloc(sizeof(HuffmanNode));
2025-01-31 16:51:34 +00:00
new_node->symbol = 0; // Symbol je irelevantný pre vnútorné uzly.
2025-01-19 18:46:52 +00:00
new_node->frequency = nodes[min1]->frequency + nodes[min2]->frequency;
new_node->left = nodes[min1];
new_node->right = nodes[min2];
2025-01-31 16:51:34 +00:00
// Aktualizácia zoznamu uzlov.
2025-01-19 18:46:52 +00:00
nodes[min1] = new_node;
nodes[min2] = nodes[node_count - 1];
node_count--;
}
2025-01-31 16:51:34 +00:00
return nodes[0]; // Návrat koreňa Huffmanovho stromu.
2025-01-19 18:46:52 +00:00
}
2025-01-31 16:51:34 +00:00
// Rekurzívna funkcia na generovanie Huffmanových kódov pre každý znak.
2025-01-19 18:46:52 +00:00
void generate_huffman_codes(HuffmanNode* root, char** codes, char* buffer, int depth) {
2025-01-31 16:51:34 +00:00
if (!root->left && !root->right) { // Ak uzol nemá potomkov, je to listový uzol.
buffer[depth] = '\0'; // Ukončenie reťazca.
codes[root->symbol] = strdup(buffer); // Uloženie kódu pre znak.
2025-01-19 18:46:52 +00:00
return;
}
if (root->left) {
2025-01-31 16:51:34 +00:00
buffer[depth] = '0'; // Pridanie '0' pre cestu doľava.
2025-01-19 18:46:52 +00:00
generate_huffman_codes(root->left, codes, buffer, depth + 1);
}
if (root->right) {
2025-01-31 16:51:34 +00:00
buffer[depth] = '1'; // Pridanie '1' pre cestu doprava.
2025-01-19 18:46:52 +00:00
generate_huffman_codes(root->right, codes, buffer, depth + 1);
}
}
2025-01-31 16:51:34 +00:00
// Funkcia na uvoľnenie pamäte alokovanej pre Huffmanov strom.
2025-01-19 18:46:52 +00:00
void free_huffman_tree(HuffmanNode* root) {
2025-01-31 16:51:34 +00:00
if (!root) return; // Ak je strom prázdny, nič nerobí.
free_huffman_tree(root->left); // Rekurzívne uvoľnenie ľavého podstromu.
free_huffman_tree(root->right); // Rekurzívne uvoľnenie pravého podstromu.
free(root); // Uvoľnenie aktuálneho uzla.
2025-01-19 18:46:52 +00:00
}
2025-01-31 16:51:34 +00:00
// Funkcia na zápis bitov do súboru pomocou dočasného bufferu.
2025-01-19 19:45:36 +00:00
void write_bits(FILE* file, const char* bits, unsigned char* buffer, int* bit_pos) {
2025-01-31 16:51:34 +00:00
for (int i = 0; bits[i] != '\0'; i++) { // Prechádza všetky bity v reťazci.
if (bits[i] == '1') { // Ak je bit '1', nastaví príslušný bit v bufferi.
2025-01-19 19:45:36 +00:00
buffer[*bit_pos / 8] |= (1 << (7 - (*bit_pos % 8)));
}
2025-01-31 16:51:34 +00:00
(*bit_pos)++; // Posunie sa na ďalšiu bitovú pozíciu.
if (*bit_pos % 8 == 0) { // Ak je buffer plný (8 bitov), zapíše sa do súboru.
2025-01-19 19:45:36 +00:00
fwrite(buffer, 1, 1, file);
2025-01-31 16:51:34 +00:00
buffer[*bit_pos / 8 - 1] = 0; // Vyprázdni buffer.
2025-01-19 19:45:36 +00:00
}
}
}
2025-01-31 16:51:34 +00:00
// Funkcia na čítanie jedného bitu zo súboru.
2025-01-19 19:45:36 +00:00
int read_bit(FILE* file, unsigned char* buffer, int* bit_pos) {
2025-01-31 16:51:34 +00:00
if (*bit_pos % 8 == 0) { // Ak sa číta nový bajt, načíta ho do bufferu.
if (fread(buffer, 1, 1, file) != 1) return -1; // Ak sa nedá čítať, vráti chybu.
2025-01-19 19:45:36 +00:00
}
2025-01-31 16:51:34 +00:00
int bit = (*buffer >> (7 - (*bit_pos % 8))) & 1; // Získa hodnotu aktuálneho bitu.
(*bit_pos)++; // Posunie sa na ďalšiu bitovú pozíciu.
2025-01-19 19:45:36 +00:00
return bit;
}
2025-01-31 16:51:34 +00:00
// Funkcia na deserializáciu Huffmanovho stromu zo súboru.
2025-01-19 19:56:27 +00:00
HuffmanNode* deserialize_tree(FILE* file) {
2025-01-31 16:51:34 +00:00
int marker = fgetc(file); // Prečíta znak z binárneho súboru.
if (marker == EOF) return NULL; // Ak je súbor na konci, vráti NULL.
2025-01-19 20:51:45 +00:00
2025-01-31 16:51:34 +00:00
if (marker == '1') { // Ak je marker '1', ide o listový uzol.
2025-01-19 19:56:27 +00:00
HuffmanNode* node = (HuffmanNode*)malloc(sizeof(HuffmanNode));
2025-01-31 16:51:34 +00:00
node->symbol = fgetc(file); // Prečíta znak listového uzla.
2025-01-19 19:56:27 +00:00
node->left = node->right = NULL;
return node;
}
2025-01-31 16:51:34 +00:00
// Ak je marker '0', ide o vnútorný uzol. Rekurzívne načíta podstromy.
2025-01-19 19:56:27 +00:00
HuffmanNode* node = (HuffmanNode*)malloc(sizeof(HuffmanNode));
node->left = deserialize_tree(file);
node->right = deserialize_tree(file);
return node;
}
2025-01-31 16:51:34 +00:00
// Funkcia na kompresiu súboru pomocou Huffmanovho kódovania.
2025-01-19 18:46:52 +00:00
int compress_1(const char* input_file_name, const char* output_file_name) {
size_t size;
2025-01-31 16:51:34 +00:00
unsigned char* data = read_file(input_file_name, &size); // Načítanie vstupného súboru.
2025-01-19 18:46:52 +00:00
if (!data) return -1;
2025-01-31 16:51:34 +00:00
HuffmanNode* root = build_huffman_tree(data, size); // Vytvorenie Huffmanovho stromu.
2025-01-19 18:46:52 +00:00
if (!root) {
free(data);
return -1;
}
2025-01-31 16:51:34 +00:00
char* codes[256] = {0}; // Pole na uloženie kódov pre každý znak.
2025-01-19 18:46:52 +00:00
char buffer[256];
2025-01-31 16:51:34 +00:00
generate_huffman_codes(root, codes, buffer, 0); // Generovanie Huffmanových kódov.
2025-01-19 18:46:52 +00:00
2025-01-31 16:51:34 +00:00
FILE* output_file = fopen(output_file_name, "wb"); // Otvorenie výstupného súboru.
2025-01-19 18:52:33 +00:00
if (!output_file) {
perror("Error opening output file");
free(data);
free_huffman_tree(root);
return -1;
}
2025-01-31 16:51:34 +00:00
unsigned char bit_buffer[1] = {0}; // Dočasný buffer na bity.
2025-01-19 19:52:36 +00:00
int bit_pos = 0;
2025-01-19 19:49:03 +00:00
2025-01-31 16:51:34 +00:00
for (size_t i = 0; i < size; i++) { // Pre každý znak vo vstupnom súbore.
write_bits(output_file, codes[data[i]], bit_buffer, &bit_pos); // Zapíše kód do súboru.
2025-01-19 19:52:36 +00:00
}
2025-01-31 16:51:34 +00:00
if (bit_pos % 8 != 0) { // Ak buffer nie je prázdny, zapíše sa jeho zvyšok.
2025-01-19 19:52:36 +00:00
fwrite(bit_buffer, 1, 1, output_file);
2025-01-19 19:40:43 +00:00
}
2025-01-31 16:51:34 +00:00
fclose(output_file); // Uzavretie výstupného súboru.
2025-01-19 18:52:33 +00:00
2025-01-19 18:46:52 +00:00
printf("Compressing using Huffman coding...\n");
for (int i = 0; i < 256; i++) {
2025-01-31 16:51:34 +00:00
if (codes[i]) free(codes[i]); // Uvoľnenie pamäte pre kódy.
2025-01-19 18:46:52 +00:00
}
2025-01-31 16:51:34 +00:00
free_huffman_tree(root); // Uvoľnenie pamäte pre Huffmanov strom.
free(data); // Uvoľnenie dátového bufferu.
2025-01-19 18:46:52 +00:00
return 0;
}
2025-01-19 18:52:33 +00:00
2025-01-19 19:52:36 +00:00
2025-01-31 16:51:34 +00:00
// Funkcia na dekompresiu súboru komprimovaného Huffmanovým kódovaním.
2025-01-19 18:46:52 +00:00
int decompress_1(const char* input_file_name, const char* output_file_name) {
2025-01-31 16:51:34 +00:00
FILE* input_file = fopen(input_file_name, "rb"); // Otvorenie vstupného súboru v binárnom režime.
2025-01-19 19:53:25 +00:00
if (!input_file) {
2025-01-31 16:51:34 +00:00
perror("Error opening input file"); // Chybové hlásenie, ak sa súbor nedá otvoriť.
2025-01-19 19:53:25 +00:00
return -1;
}
2025-01-19 19:45:36 +00:00
2025-01-31 16:51:34 +00:00
FILE* output_file = fopen(output_file_name, "wb"); // Otvorenie výstupného súboru v binárnom režime.
2025-01-19 19:53:25 +00:00
if (!output_file) {
perror("Error opening output file");
fclose(input_file);
return -1;
}
2025-01-19 18:46:52 +00:00
2025-01-31 16:51:34 +00:00
HuffmanNode* root = deserialize_tree(input_file); // Načítanie Huffmanovho stromu zo súboru.
2025-01-19 19:53:25 +00:00
if (!root) {
fclose(input_file);
fclose(output_file);
return -1;
}
2025-01-31 16:51:34 +00:00
unsigned char bit_buffer[1]; // Buffer na čítanie bitov.
2025-01-19 19:53:25 +00:00
int bit_pos = 0;
2025-01-31 16:51:34 +00:00
HuffmanNode* current = root; // Počiatočný uzol stromu.
2025-01-19 19:53:25 +00:00
while (1) {
2025-01-31 16:51:34 +00:00
int bit = read_bit(input_file, bit_buffer, &bit_pos); // Čítanie bitu zo súboru.
if (bit == -1) break; // Ak je koniec súboru, ukončí sa čítanie.
2025-01-19 19:53:25 +00:00
2025-01-31 16:51:34 +00:00
current = (bit == 0) ? current->left : current->right; // Posun v strome na základe bitu.
2025-01-19 19:53:25 +00:00
2025-01-31 16:51:34 +00:00
if (!current->left && !current->right) { // Ak je uzol listový, znamená to dekódovaný znak.
fputc(current->symbol, output_file); // Zapíše sa znak do výstupného súboru.
current = root; // Návrat na koreň stromu pre ďalší znak.
2025-01-19 19:53:25 +00:00
}
}
2025-01-31 16:51:34 +00:00
fclose(input_file); // Uzavretie vstupného súboru.
fclose(output_file); // Uzavretie výstupného súboru.
free_huffman_tree(root); // Uvoľnenie pamäte Huffmanovho stromu.
2025-01-19 19:53:25 +00:00
printf("Decompressing using Huffman coding...\n");
return 0;
2025-01-19 19:49:03 +00:00
}
2025-01-19 19:45:36 +00:00
2025-01-31 16:51:34 +00:00
// Funkcia na kompresiu súboru pomocou LZ77 algoritmu.
2025-01-19 18:46:52 +00:00
int compress_2(const char* input_file_name, const char* output_file_name) {
size_t size;
2025-01-31 16:51:34 +00:00
unsigned char* data = read_file(input_file_name, &size); // Načítanie obsahu vstupného súboru.
2025-01-19 18:46:52 +00:00
if (!data) return -1;
2025-01-31 16:51:34 +00:00
FILE* output_file = fopen(output_file_name, "wb"); // Otvorenie výstupného súboru v binárnom režime.
2025-01-19 20:45:12 +00:00
if (!output_file) {
perror("Error opening output file");
free(data);
return -1;
}
2025-01-31 16:51:34 +00:00
size_t search_buffer_size = 1024; // Veľkosť vyhľadávacieho okna.
size_t lookahead_buffer_size = 16; // Veľkosť prehliadacieho okna.
2025-01-19 20:45:12 +00:00
size_t pos = 0;
while (pos < size) {
2025-01-31 16:51:34 +00:00
size_t best_match_offset = 0; // Najlepší posun pre aktuálny segment.
size_t best_match_length = 0; // Dĺžka najlepšieho zhody.
2025-01-19 20:45:12 +00:00
2025-01-31 16:51:34 +00:00
// Vyhľadávanie najdlhšej zhody v rámci vyhľadávacieho okna.
2025-01-19 20:45:12 +00:00
for (size_t i = (pos > search_buffer_size ? pos - search_buffer_size : 0); i < pos; i++) {
size_t match_length = 0;
while (match_length < lookahead_buffer_size &&
pos + match_length < size &&
data[i + match_length] == data[pos + match_length]) {
2025-01-31 16:51:34 +00:00
match_length++; // Počítanie dĺžky zhody.
2025-01-19 20:45:12 +00:00
}
2025-01-31 16:51:34 +00:00
if (match_length > best_match_length) { // Aktualizácia najlepšieho zhody.
2025-01-19 20:45:12 +00:00
best_match_length = match_length;
best_match_offset = pos - i;
}
}
2025-01-31 16:51:34 +00:00
// Zápis zhody a nasledujúceho symbolu do súboru.
unsigned char offset_high = (best_match_offset >> 8) & 0xFF; // Vyšších 8 bitov posunu.
unsigned char offset_low = best_match_offset & 0xFF; // Nižších 8 bitov posunu.
unsigned char length = best_match_length; // Dĺžka zhody.
unsigned char next_symbol = (pos + best_match_length < size) ? data[pos + best_match_length] : 0; // Nasledujúci symbol.
2025-01-19 20:45:12 +00:00
fputc(offset_high, output_file);
fputc(offset_low, output_file);
fputc(length, output_file);
fputc(next_symbol, output_file);
2025-01-31 16:51:34 +00:00
pos += best_match_length + 1; // Posun v prehliadacom okne.
2025-01-19 20:45:12 +00:00
}
2025-01-31 16:51:34 +00:00
fclose(output_file); // Uzavretie výstupného súboru.
free(data); // Uvoľnenie načítaných dát.
2025-01-19 20:45:12 +00:00
printf("Compression using LZ77 completed successfully.\n");
return 0;
2025-01-19 18:46:52 +00:00
}
2025-01-31 16:51:34 +00:00
// Funkcia na dekompresiu súboru komprimovaného LZ77 algoritmom.
2025-01-19 18:46:52 +00:00
int decompress_2(const char* input_file_name, const char* output_file_name) {
2025-01-31 16:51:34 +00:00
FILE* input_file = fopen(input_file_name, "rb"); // Otvorenie vstupného súboru v binárnom režime.
2025-01-19 20:45:12 +00:00
if (!input_file) {
perror("Error opening input file");
return -1;
}
2025-01-19 18:46:52 +00:00
2025-01-31 16:51:34 +00:00
FILE* output_file = fopen(output_file_name, "wb"); // Otvorenie výstupného súboru v binárnom režime.
2025-01-19 20:45:12 +00:00
if (!output_file) {
perror("Error opening output file");
fclose(input_file);
return -1;
}
2025-01-19 18:46:52 +00:00
2025-01-31 16:51:34 +00:00
unsigned char* output_buffer = (unsigned char*)malloc(10 * 1024 * 1024); // Alokácia bufferu na dekomprimované dáta (max. 10 MB).
2025-01-19 20:45:12 +00:00
if (!output_buffer) {
perror("Memory allocation error");
fclose(input_file);
fclose(output_file);
return -1;
}
size_t pos = 0;
while (1) {
2025-01-31 16:51:34 +00:00
// Čítanie posunu, dĺžky a nasledujúceho symbolu zo vstupného súboru.
2025-01-19 20:45:12 +00:00
int offset_high = fgetc(input_file);
int offset_low = fgetc(input_file);
int length = fgetc(input_file);
int next_symbol = fgetc(input_file);
2025-01-31 16:51:34 +00:00
if (offset_high == EOF || offset_low == EOF || length == EOF || next_symbol == EOF) break; // Koniec súboru.
2025-01-19 20:45:12 +00:00
2025-01-31 16:51:34 +00:00
size_t offset = (offset_high << 8) | offset_low; // Zloženie posunu z vyšších a nižších bitov.
2025-01-19 20:51:45 +00:00
2025-01-31 16:51:34 +00:00
// Obnovenie zhody z výstupného bufferu.
2025-01-19 20:45:12 +00:00
for (size_t i = 0; i < length; i++) {
output_buffer[pos] = output_buffer[pos - offset];
pos++;
}
2025-01-31 16:51:34 +00:00
// Zapísanie nasledujúceho symbolu do bufferu.
2025-01-19 20:45:12 +00:00
output_buffer[pos++] = next_symbol;
}
2025-01-31 16:51:34 +00:00
fwrite(output_buffer, 1, pos, output_file); // Zápis dekomprimovaných dát do súboru.
2025-01-19 20:45:12 +00:00
2025-01-31 16:51:34 +00:00
free(output_buffer); // Uvoľnenie alokovaného bufferu.
fclose(input_file); // Uzavretie vstupného súboru.
fclose(output_file); // Uzavretie výstupného súboru.
2025-01-19 20:45:12 +00:00
printf("Decompression using LZ77 completed successfully.\n");
return 0;
2025-01-31 16:51:34 +00:00
}