137 lines
3.5 KiB
C
137 lines
3.5 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*, FILE*);
|
|
|
|
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* input, FILE* output) {
|
|
Buffer buffer = createBuffer(output);
|
|
int current;
|
|
|
|
// Lecture du fichier d'entrée caractère par caractère
|
|
while ((current = fgetc(input)) != 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', &buffer);
|
|
label++;
|
|
}
|
|
}
|
|
|
|
// Écriture du dernier octet dans le fichier
|
|
// (pas toujours plein)
|
|
flushBuffer(&buffer);
|
|
}
|
|
|
|
int compress(const char* inputpath, const char* outputpath) {
|
|
// FIXME: gérer le fichier vide
|
|
// FIXME: gérer les fichiers avec un seul type de caractère
|
|
|
|
printVerbose(
|
|
"Compression du fichier '%s' en '%s'...\n",
|
|
inputpath, outputpath
|
|
);
|
|
|
|
// Ouverture de l'entrée en lecture et la sortie en écriture
|
|
FILE* input;
|
|
FILE* output;
|
|
|
|
if ((input = fopen(inputpath, "r")) == NULL) {
|
|
return COMPRESS_OPEN_INPUT_ERROR;
|
|
}
|
|
|
|
if ((output = fopen(outputpath, "w")) == NULL) {
|
|
return COMPRESS_OPEN_OUTPUT_ERROR;
|
|
}
|
|
|
|
// Comptage des caractères et calcul des fréquences d'apparition
|
|
printVerbose("Calcul des fréquences d'apparition des caractères.\n");
|
|
double* frequencies = _createFrequencies(input);
|
|
|
|
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);
|
|
}
|
|
|
|
printVerbose("\nÉcriture de l'arbre dans la sortie.\n");
|
|
writeTree(tree, output);
|
|
|
|
printVerbose("Écriture des données compressées du fichier.\n");
|
|
rewind(input);
|
|
_encodeFromTable(labels, input, output);
|
|
|
|
freeTree(tree);
|
|
freeTreeLabels(labels);
|
|
labels = NULL;
|
|
|
|
fclose(input);
|
|
fclose(output);
|
|
|
|
return COMPRESS_OK;
|
|
}
|