diff --git a/src/compress.c b/src/compress.c index a5873b0..b5385eb 100644 --- a/src/compress.c +++ b/src/compress.c @@ -11,24 +11,23 @@ #include /** - * 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 + * 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*); +static double* _createFrequencies(FILE*, size_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() */ -void _encodeFromTable(char**, FILE*, Buffer*); +static void _encodeFromTable(char**, FILE*, WriteBuffer*); -double* _createFrequencies(FILE* file) { +double* _createFrequencies(FILE* file, size_t* total) { double* frequencies = malloc(NUM_CHARS * sizeof(*frequencies)); - int totalChars = 0; + *total = 0; for (size_t i = 0; i < NUM_CHARS; i++) { frequencies[i] = 0; @@ -40,18 +39,18 @@ double* _createFrequencies(FILE* file) { while ((current = fgetc(file)) != EOF) { assert(current >= 0 && current < NUM_CHARS); frequencies[current]++; - totalChars++; + (*total)++; } // Conversion des effectifs des caractères en fréquences for (size_t i = 0; i < NUM_CHARS; i++) { - frequencies[i] /= totalChars; + frequencies[i] /= *total; } return frequencies; } -void _encodeFromTable(char** labels, FILE* source, Buffer* output) { +void _encodeFromTable(char** labels, FILE* source, WriteBuffer* output) { int current; // Lecture du fichier d'entrée caractère par caractère @@ -63,25 +62,26 @@ void _encodeFromTable(char** labels, FILE* source, Buffer* output) { // Ajout du label dans le buffer, caractère par caractère // vidant progressivement le buffer while (*label != '\0') { - pushToBuffer(*label == '1', output); + putBuffer(*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 + // Étape 1 : calcul des fréquences de chaque caractère + // FIXME: éviter la division par zéro pour les fichiers vides printVerbose("Calcul des fréquences d'apparition des caractères.\n"); - double* frequencies = _createFrequencies(source); + size_t start_bytes = 0; + double* frequencies = _createFrequencies(source, &start_bytes); if (isVerbose()) { printFrequenciesTable(frequencies, NUM_CHARS); } + // Étape 2 : construction d'un arbre de Huffman pour les fréquences + // 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); @@ -92,6 +92,7 @@ void compress(FILE* source, FILE* dest) { printTree(tree); } + // Étape 3 : calcul de la clef de codage (coloration de l'arbre) printVerbose("\nÉtiquetage des feuilles de l'arbre.\n"); char** labels = createTreeLabels(tree); @@ -99,18 +100,31 @@ void compress(FILE* source, FILE* dest) { printLabelsTable(labels, NUM_CHARS); } - // Écriture des données compressées vers la sortie - Buffer output = createBuffer(dest); + // Étape 4 : écriture des données compressées vers la sortie + WriteBuffer output = createWriteBuffer(dest); + printVerbose("\nÉcriture des données compressées.\n"); - printVerbose("\nÉcriture de l'arbre dans la sortie.\n"); + // - Entier : 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); - printVerbose("Écriture des données compressées du fichier.\n"); + // - Données compressées brutes (caractères originels traduits dans + // la clef de codage calculée avant) rewind(source); _encodeFromTable(labels, source, &output); - - // Vidage du dernier octet du tampon et nettoyage flushBuffer(&output); + + // Affichage des statistiques de compression + size_t end_bytes = sizeof(start_bytes) + getFlushedCount(&output); + double gain = (start_bytes - end_bytes) / (double) start_bytes * 100; + + printVerbose("Taille originelle : %d octets.\n", start_bytes); + printVerbose("Taille compressée : %d octets.\n", end_bytes); + printVerbose("Gain : %.2f %% !\n", gain); + + // Nettoyage des objets freeTree(tree); freeTreeLabels(labels); labels = NULL;