huffman/src/compress.c

116 lines
3.1 KiB
C

#include "compress.h"
#include "common.h"
#include "buffer.h"
#include "display.h"
#include "assert.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
/**
* Calculer un tableau de fréquences d'apparition des
* caractères ASCII dans le fichier donné. Le fichier
* donné doit être ouvert au moins en lecture
*
* (résultat à libérer avec `free`)
*/
static double* _createFrequencies(FILE*);
/**
* Encoder le fichier d'entrée vers le fichier de sortie
* en suivant les étiquettes passées. Les étiquettes
* peuvent être calculées avec labelTree()
*/
void _encodeFromTable(char**, FILE*, Buffer*);
double* _createFrequencies(FILE* file) {
double* frequencies = malloc(NUM_CHARS * sizeof(*frequencies));
int totalChars = 0;
for (size_t i = 0; i < NUM_CHARS; i++) {
frequencies[i] = 0;
}
int current;
// Lecture du fichier caractère par caractère et comptage
while ((current = fgetc(file)) != EOF) {
assert(current >= 0 && current < NUM_CHARS);
frequencies[current]++;
totalChars++;
}
// Conversion des effectifs des caractères en fréquences
for (size_t i = 0; i < NUM_CHARS; i++) {
frequencies[i] /= totalChars;
}
return frequencies;
}
void _encodeFromTable(char** labels, FILE* source, Buffer* output) {
int current;
// Lecture du fichier d'entrée caractère par caractère
while ((current = fgetc(source)) != EOF) {
assert(current >= 0 && current < NUM_CHARS);
char* label = labels[current];
assert(label != NULL);
// Ajout du label dans le buffer, caractère par caractère
// vidant progressivement le buffer
while (*label != '\0') {
pushToBuffer(*label == '1', output);
label++;
}
}
}
void compress(FILE* source, FILE* dest) {
// FIXME: gérer le fichier vide
// FIXME: gérer les fichiers ne contenant qu'un seul type de caractère
// FIXME: gérer l'entrée depuis stdin (pas de double lecture)
// Calcul de la clef de codage à partir de la source
printVerbose("Calcul des fréquences d'apparition des caractères.\n");
double* frequencies = _createFrequencies(source);
if (isVerbose()) {
printFrequenciesTable(frequencies, NUM_CHARS);
}
printVerbose("\nConstruction de l'arbre de Huffman.\n");
HufTree tree = createTree(frequencies);
free(frequencies);
frequencies = NULL;
if (isVerbose()) {
printTree(tree);
}
printVerbose("\nÉtiquetage des feuilles de l'arbre.\n");
char** labels = createTreeLabels(tree);
if (isVerbose()) {
printLabelsTable(labels, NUM_CHARS);
}
// Écriture des données compressées vers la sortie
Buffer output = createBuffer(dest);
printVerbose("\nÉcriture de l'arbre dans la sortie.\n");
writeTree(tree, &output);
printVerbose("Écriture des données compressées du fichier.\n");
rewind(source);
_encodeFromTable(labels, source, &output);
// Vidage du dernier octet du tampon et nettoyage
flushBuffer(&output);
freeTree(tree);
freeTreeLabels(labels);
labels = NULL;
}