#include "arbre.h" int listeVersArbre(Liste *liste) { Cellule *curseur = *liste; int nombreLettresDansFichier = 0; while (curseur != NULL) { // parcours de la liste if (curseur->lettre != '\0') nombreLettresDansFichier++; // +1 au compteur si c'est bien une lettre if (curseur->suivant != NULL) { // on créer un mini-arbre qu'on colle au reste de l'arbre seulement si le suivant existe Cellule *nouvelleCellule; if ((nouvelleCellule = (Cellule*)malloc(sizeof(Cellule))) == NULL) { // on alloue de la mémoire pour notre nouvelle cellule (mini-racine) printf("Impossible d'allouer de la mémoire supplémentaire (listeVersArbre).\n"); // gestion de l'erreur exit(1); } nouvelleCellule->gauche = curseur; // membre gauche = curseur nouvelleCellule->droite = curseur->suivant; // membre droit = élément suivant dans la liste nouvelleCellule->lettre = '\0'; nouvelleCellule->frequence = nouvelleCellule->gauche->frequence + nouvelleCellule->droite->frequence; if (curseur->suivant->lettre != '\0') nombreLettresDansFichier++; // +1 si le suivant est aussi une lettre (membre droit) curseur = curseur->suivant->suivant; // on va au suivant 2x car on a déjà ajouté le suivant en tant que membre droit *liste = curseur; // on change le point de départ de notre liste pour ne pas traiter en boucle les anciennes cellules ajouterRangee(liste, nouvelleCellule); // on ajoute à la liste notre nouvelle cellule curseur = *liste; // on met à jour le curseur } else { // cas du dernier élément *liste = curseur; curseur = curseur->suivant; } } return nombreLettresDansFichier; } void assignationCode(Arbre arbre, int codeActuel, int longueur, Entete *enteteListe, int *i, int *longueurTotale) { if (arbre->lettre != '\0') { // si c'est une lettre qu'on regarde enteteListe[*i].lettre = arbre->lettre; // ajout de la lettre enteteListe[*i].code = codeActuel; // assignation de son code enteteListe[*i].longueur = longueur; // longueur du code (ex: 1001 est de taille 4) *i += 1; // on incrémente de 1 *longueurTotale += longueur * arbre->frequence; // incrémente la taille du code à la taille totale finale } else { // si c'est une "mini-racine" longueur++; codeActuel <<= 1; // décalage de bit vers la gauche assignationCode(arbre->gauche, codeActuel, longueur, enteteListe, i, longueurTotale); // appel récursif arbre de gauche codeActuel |= 1; // copie de bit si nécessaire (porte ou) assignationCode(arbre->droite, codeActuel, longueur, enteteListe, i, longueurTotale); // appel récursif arbre de droite } } // Libère en mémoire un Arbre void freeArbre(Arbre arbre) { if (arbre->lettre == '\0') { // free aussi les mini-racines freeArbre(arbre->gauche); freeArbre(arbre->droite); } free(arbre); // free du noeud courant } Entete *arbreVersListe(Arbre arbre, int taille, int *tailleTotale) { Entete *enteteListe; if ((enteteListe = (Entete*)malloc(taille * sizeof(Entete))) == NULL) { // on alloue la liste qui va contenir nos caractères printf("Impossible d'allouer de la mémoire supplémentaire (arbreVersListe).\n"); // gestion de l'erreur exit(1); } int i = 0; // initialisation de `i` au début car `assignationCode` est récursif assignationCode(arbre, '\0', 0, enteteListe, &i, tailleTotale); // on commence avec une racine nulle et une taille de 0 freeArbre(arbre); return enteteListe; } Entete *fichierVersListe(FILE *fichier, int *nombreLettresDansFichier, int *tailleTotale) { char lettre = 'a'; // initalisation pour éviter un warning Liste liste = NULL; while (lettre != EOF) { lettre = fgetc(fichier); ajouterLettre(&liste, lettre); } rewind(fichier); trierListe(&liste); *nombreLettresDansFichier = listeVersArbre(&liste); return arbreVersListe(liste, *nombreLettresDansFichier, tailleTotale); } void compression(FILE *entree, FILE *sortie) { int nombreLettresDansFichier; // initialisé par `listeVersArbre` int tailleTotale = 0; // taille totale en en bit du fichier en sortie Entete *enteteListe = fichierVersListe(entree, &nombreLettresDansFichier, &tailleTotale); // On écrit l'entête du fichier avec la table complète des correspondances enteteVersFichier(enteteListe, nombreLettresDansFichier, tailleTotale, sortie); // On écrit les données huffman-isée dans le fichier huffmanVersFichier(entree, sortie, enteteListe, nombreLettresDansFichier); free(enteteListe); // libération de la liste car utilisation du malloc dans `arbreVersListe` } void enteteVersFichier(Entete *enteteListe, int nombreLettresDansFichier, int longueurTotale, FILE *fichier) { fwrite(&nombreLettresDansFichier, sizeof(int), 1, fichier); // stockage du nombre de lettres dans le fichier fwrite(&longueurTotale, sizeof(int), 1, fichier); // stockage de la taille totale for (int i = 0; i < nombreLettresDansFichier; i++) { // on parcours l'entete et on y ajoute la table de huffman utilisé fwrite(&(enteteListe[i].lettre), sizeof(char), 1, fichier); fwrite(&(enteteListe[i].code), sizeof(int), 1, fichier); fwrite(&(enteteListe[i].longueur), sizeof(int), 1, fichier); } } void huffmanVersFichier(FILE *entree, FILE *sortie, Entete *enteteListe, int nombreLettresDansFichier) { int buffer = 0, tailleBufferActuelle = 0, sizeOfInt = sizeof(int) * 8; // *8 car "long unsigned int" d'après GCC char lettre = 'a'; // initalisation pour éviter un warning while (lettre != EOF) { lettre = fgetc(entree); Entete entete = recuperationLettre(lettre, enteteListe, nombreLettresDansFichier); // rappel: entete représente une liste de caractères if (tailleBufferActuelle + entete.longueur >= sizeOfInt) { // écriture dans le fichier // Modification du buffer int aAjouter = sizeOfInt - tailleBufferActuelle; buffer <<= aAjouter; // Ajout de la lettre int tmp = entete.code >> (entete.longueur - aAjouter); entete.longueur -= aAjouter; // Écriture dans le fichier ici buffer |= tmp; fwrite(&buffer, sizeof(int), 1, sortie); buffer = 0, tailleBufferActuelle = 0; // reset du buffer } } if (tailleBufferActuelle > 0) { buffer <<= sizeOfInt - tailleBufferActuelle; // décalage vers la gauche fwrite(&buffer, sizeof(int), 1, sortie); } } Entete recuperationLettre(char lettre, Entete *enteteListe, int nombreLettresDansFichier) { for (int i = 0; i < nombreLettresDansFichier; i++) if (enteteListe[i].lettre == lettre) return enteteListe[i]; exit(1); } void decompression(FILE *entree, FILE *sortie) { /* fclose temporaire pour supprimer les warnings */ fclose(entree); fclose(sortie); }