#include "common.h" #include "compress.h" #include "buffer.h" #include "huftree.h" #include "display.h" #include #include #include #include #include #include #include /** * Effectuer un comptage des caractères dans le corpus d'entrée. En déduire * le tableau des fréquences d'apparition des caractères ASCII * * (résultat à libérer avec `free`) */ static double* _createFrequencies(FILE*, uint64_t*); /** * 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() */ static void _encodeFromTable(char**, FILE*, WriteBuffer*); double* _createFrequencies(FILE* file, uint64_t* total) { double* frequencies = malloc(NUM_CHARS * sizeof(*frequencies)); *total = 0; for (size_t i = 0; i < NUM_CHARS; i++) { frequencies[i] = 0; } // Lecture du fichier caractère par caractère et comptage int current; while ((current = fgetc(file)) != EOF) { assert(current >= 0 && current < NUM_CHARS); frequencies[current]++; (*total)++; } // Conversion des effectifs des caractères en fréquences for (size_t i = 0; i < NUM_CHARS; i++) { frequencies[i] /= *total; } return frequencies; } void _encodeFromTable(char** labels, FILE* source, WriteBuffer* 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') { putBuffer(*label == '1', output); label++; } } } void compress(FILE* source, FILE* dest) { // ÉTAPE 1 : calcul des fréquences d'apparition de chaque caractère // dans le fichier source. Ce programme prend le terme caractère au sens // restreint d'octet. Dans le cas où l'on compresse des fichiers Unicode // utilisant des caractères sur plusieurs octets, on aura donc une // compression moins optimale. // FIXME: éviter la division par zéro pour les fichiers vides printVerbose("Calcul des fréquences d'apparition des caractères.\n"); uint64_t start_bytes = 0; double* frequencies = _createFrequencies(source, &start_bytes); if (isVerbose()) { printFrequenciesTable(frequencies, NUM_CHARS); } // ÉTAPE 2 : construction d'un arbre de Huffman correspondant aux // fréquences d'apparition des caractères // FIXME: éviter les arbres malformés avec les fichiers ne contenant qu'un // seul type de caractère printVerbose("\nConstruction de l'arbre de Huffman.\n"); HufTree tree = createTree(frequencies); free(frequencies); frequencies = NULL; if (isVerbose()) { printTree(tree); } // ÉTAPE 3 : calcul de la clef de codage en étiquettant les chaque feuille // de l'arbre. Pour chaque sommet traversé, s'il est le fils gauche de son // parent, l'étiquette est augmentée d'un '0', sinon elle l'est d'un '1' printVerbose("\nÉtiquetage des feuilles de l'arbre.\n"); char** labels = createTreeLabels(tree); if (isVerbose()) { printLabelsTable(labels, NUM_CHARS); } // ÉTAPE 4 : écriture des données compressées vers la sortie. Les données // écrites permettent de restituer le fichier originel : nombre de // caractères stockés, arbre de Huffman et données compressées brutes WriteBuffer output = createWriteBuffer(dest); printVerbose("\nÉcriture des données compressées.\n"); // - Entier 64 bits : nombre d'octets dans le fichier originel fwrite(&start_bytes, sizeof(start_bytes), 1, dest); // - Arbre linéarisé : arbre de Huffman permettant le calcul de la clef writeTree(tree, &output); // - Données compressées brutes (caractères originels traduits dans // la clef de codage calculée avant) rewind(source); _encodeFromTable(labels, source, &output); flushBuffer(&output); // ÉTAPE 5 : affichage des statistiques de compression et fin uint64_t end_bytes = sizeof(start_bytes) + getFlushedCount(&output); double gain = ((double) start_bytes - end_bytes) / start_bytes * 100; printVerbose("Taille originelle : %" PRIu64 " octets.\n", start_bytes); printVerbose("Taille compressée : %" PRIu64 " octets.\n", end_bytes); printVerbose("Gain : %.2f %% !\n", gain); freeTree(tree); freeTreeLabels(labels); labels = NULL; }