This commit is contained in:
Your Name 2026-01-11 21:32:18 +01:00
parent 9bb436a453
commit 44e8945215
8 changed files with 393 additions and 0 deletions

17
sk1/Makefile Normal file
View File

@ -0,0 +1,17 @@
CC = gcc
CFLAGS = -std=c11 -Wall -Wextra -O2
all: compressor
compressor: main.o compressor.o
$(CC) $(CFLAGS) -o compressor main.o compressor.o
main.o: main.c compressor.h
$(CC) $(CFLAGS) -c main.c
compressor.o: compressor.c compressor.h
$(CC) $(CFLAGS) -c compressor.c
clean:
rm -f *.o compressor

82
sk1/README.md Normal file
View File

@ -0,0 +1,82 @@
# Kompressor domáca časť skúšky
## Zadanie
Cieľom zadania je naprogramovať nástroj na kompresiu a dekompresiu súborov.
Program musí fungovať pre ľubovoľné binárne súbory do veľkosti 10 MB.
Po dekompresii musí byť výsledný súbor rovnaký ako pôvodný.
Nie je povolené použiť RLE (Run Length Encoding).
Je povolené použiť napríklad Huffmanovo kódovanie alebo iný algoritmus.
---
## Popis programu
Program umožňuje:
- skomprimovať súbor
- dekomprimovať súbor
- vypísať pomoc pri spustení
Program sa spúšťa z príkazového riadka a pracuje so súbormi zadanými ako argumenty.
---
## Použitý algoritmus
Použil som **Huffmanovo kódovanie**.
Najprv sa spočítajú frekvencie jednotlivých bajtov vo vstupnom súbore.
Podľa frekvencií sa vytvorí Huffmanov strom.
Každému bajtu sa priradí bitový kód.
Do výsledného súboru sa uloží:
- veľkosť pôvodného súboru
- tabuľka frekvencií
- skomprimovaný bitový tok
Pri dekompresii sa z tabuľky frekvencií znovu vytvorí strom
a pomocou neho sa dáta dekódujú späť.
---
## Použitie programu
./compressor -c vstupny_subor vystupny_subor
Skomprimuje vstupný súbor do výstupného.
./compressor -d skomprimovany_subor vystupny_subor
Dekompresia späť na pôvodné dáta.
./compressor -h
Vypíše pomoc.
---
## Technické detaily
- program je napísaný v jazyku C (štandard C11)
- používa iba štandardnú knižnicu C
- práca so súbormi je realizovaná pomocou fopen, fread a fwrite
- program je rozdelený na viac súborov:
- main.c spracovanie argumentov a spustenie programu
- compressor.c implementácia kompresie a dekompresie
- compressor.h hlavičkový súbor
- preklad prebieha pomocou Makefile
---
## Obmedzenia
- maximálna veľkosť vstupného súboru je 10 MB
- kompresia nemusí byť efektívna pre všetky typy súborov
- program nevyužíva žiadne externé knižnice
---
## Testovanie
Program bol testovaný na súboroch z Canterbury corpus.
Po dekompresii bol výsledný súbor vždy zhodný s pôvodným.
---
## Použité zdroje
- študijné materiály k predmetu
- základný popis Huffmanovho kódovania
- generatívny model ChatGPT (OpenAI)
Použitý prompt:
"Implement Huffman compression in C without external libraries"

BIN
sk1/compressor Executable file

Binary file not shown.

249
sk1/compressor.c Normal file
View File

@ -0,0 +1,249 @@
#include "compressor.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#define BYTE_RANGE 256
#define IO_BUFFER 8192
typedef struct HuffNode {
int value;
uint32_t weight;
struct HuffNode *zero;
struct HuffNode *one;
} HuffNode;
typedef struct {
uint32_t pattern;
uint8_t size;
} BitCode;
static HuffNode *node_create(int value, uint32_t weight)
{
HuffNode *n=malloc(sizeof(HuffNode));
if (!n)
{
return NULL;
}
n->value=value;
n->weight=weight;
n->zero=NULL;
n->one=NULL;
return n;
}
static void node_destroy(HuffNode *n)
{
if (!n)
{
return;
}
node_destroy(n->zero);
node_destroy(n->one);
free(n);
}
static HuffNode *assemble_tree(uint32_t histogram[])
{
HuffNode *pool[BYTE_RANGE*2];
int pool_size=0;
for (int i = 0; i < BYTE_RANGE; i++)
{
if (histogram[i]>0)
{
pool[pool_size++] = node_create(i, histogram[i]);
}
}
if (pool_size==1)
{
pool[pool_size++]=node_create(-1, 0);
}
while (pool_size>1)
{
int m1=0, m2=1;
if (pool[m2]->weight<pool[m1]->weight)
{
int t=m1; m1=m2; m2=t;
}
for (int i = 2; i < pool_size; i++)
{
if (pool[i]->weight < pool[m1]->weight)
{
m2=m1;
m1=i;
} else if (pool[i]->weight < pool[m2]->weight)
{
m2=i;
}
}
HuffNode *parent = node_create(
-1,
pool[m1]->weight+pool[m2]->weight
);
parent->zero=pool[m1];
parent->one=pool[m2];
pool[m1]=parent;
pool[m2]=pool[pool_size-1];
pool_size--;
}
return pool[0];
}
static void generate_codes
(
HuffNode *root,
BitCode table[],
uint32_t bits,
uint8_t depth
) {
if (!root) return;
if (root->value >= 0)
{
table[root->value].pattern=bits;
table[root->value].size=depth;
return;
}
generate_codes(root->zero, table, bits << 1, depth + 1);
generate_codes(root->one, table, (bits << 1) | 1, depth + 1);
}
int pack_binary(const char *input_path, const char *output_path)
{
FILE *src=fopen(input_path, "rb");
FILE *dst=fopen(output_path, "wb");
if (!src || !dst)
{
if (src) fclose(src);
if (dst) fclose(dst);
return 1;
}
uint32_t freq_map[BYTE_RANGE]={0};
uint8_t buffer[IO_BUFFER];
size_t read_bytes;
uint32_t total_size=0;
while ((read_bytes = fread(buffer, 1, IO_BUFFER, src)) > 0)
{
total_size += read_bytes;
for (size_t i = 0; i < read_bytes; i++)
{
freq_map[buffer[i]]++;
}
}
rewind(src);
if (fwrite(&total_size, sizeof(uint32_t), 1, dst) != 1 ||
fwrite(freq_map, sizeof(uint32_t), BYTE_RANGE, dst) != BYTE_RANGE)
{
fclose(src);
fclose(dst);
return 1;
}
HuffNode *tree=assemble_tree(freq_map);
if (!tree)
{
fclose(src);
fclose(dst);
return 1;
}
BitCode codebook[BYTE_RANGE];
memset(codebook, 0, sizeof(codebook));
generate_codes(tree, codebook, 0, 0);
uint8_t out_byte=0;
int bit_count=0;
while ((read_bytes = fread(buffer, 1, IO_BUFFER, src)) > 0) {
for (size_t i = 0; i < read_bytes; i++)
{
BitCode c=codebook[buffer[i]];
for (int b = c.size - 1; b >= 0; b--)
{
out_byte = (out_byte << 1) |
((c.pattern >> b) & 1);
bit_count++;
if (bit_count==8)
{
if (fwrite(&out_byte, 1, 1, dst)!=1)
{
node_destroy(tree);
fclose(src);
fclose(dst);
return 1;
}
out_byte=0;
bit_count=0;
}
}
}
}
if (bit_count>0)
{
out_byte <<= (8 - bit_count);
if (fwrite(&out_byte, 1, 1, dst)!=1)
{
node_destroy(tree);
fclose(src);
fclose(dst);
return 1;
}
}
node_destroy(tree);
fclose(src);
fclose(dst);
return 0;
}
int unpack_binary(const char *input_path, const char *output_path)
{
FILE *src=fopen(input_path, "rb");
FILE *dst=fopen(output_path, "wb");
if (!src || !dst)
{
if (src) fclose(src);
if (dst) fclose(dst);
return 1;
}
uint32_t expected_size;
uint32_t freq_map[BYTE_RANGE];
if (fread(&expected_size, sizeof(uint32_t), 1, src) != 1 ||
fread(freq_map, sizeof(uint32_t), BYTE_RANGE, src) != BYTE_RANGE)
{
fclose(src);
fclose(dst);
return 1;
}
HuffNode *tree=assemble_tree(freq_map);
if (!tree)
{
fclose(src);
fclose(dst);
return 1;
}
HuffNode *cursor=tree;
uint8_t byte;
uint32_t produced=0;
while (produced<expected_size &&
fread(&byte, 1, 1, src)==1)
{
for (int i = 7; i >= 0 && produced < expected_size; i--)
{
int bit=(byte >> i) & 1;
cursor=bit ? cursor->one : cursor->zero;
if (cursor->value>=0)
{
if (fputc(cursor->value, dst)==EOF)
{
node_destroy(tree);
fclose(src);
fclose(dst);
return 1;
}
produced++;
cursor=tree;
}
}
}
node_destroy(tree);
fclose(src);
fclose(dst);
return 0;
}

7
sk1/compressor.h Normal file
View File

@ -0,0 +1,7 @@
#ifndef COMPRESSOR_H
#define COMPRESSOR_H
int pack_binary(const char *input_path, const char *output_path);
int unpack_binary(const char *input_path, const char *output_path);
#endif

BIN
sk1/compressor.o Normal file

Binary file not shown.

38
sk1/main.c Normal file
View File

@ -0,0 +1,38 @@
#include "compressor.h"
#include <stdio.h>
#include <string.h>
static void print_help(void) {
printf("Binary compressor using Huffman coding\n");
printf("Usage:\n");
printf(" ./compressor -c input output\n");
printf(" ./compressor -d input output\n");
printf(" ./compressor -h\n");
}
int main(int argc, char *argv[]) {
if (argc < 2) {
print_help();
return 1;
}
if (strcmp(argv[1], "-h") == 0) {
print_help();
return 0;
}
if (argc != 4) {
print_help();
return 1;
}
if (strcmp(argv[1], "-c") == 0)
return pack_binary(argv[2], argv[3]);
if (strcmp(argv[1], "-d") == 0)
return unpack_binary(argv[2], argv[3]);
print_help();
return 1;
}

BIN
sk1/main.o Normal file

Binary file not shown.