2021-12-18 13:51:10 +01:00
# include "arbre.h"
2021-12-24 15:45:43 +01:00
int listeVersArbre ( Liste * liste ) {
2021-12-20 17:10:35 +01:00
Cellule * curseur = * liste ;
2021-12-24 15:45:43 +01:00
int nombreLettresDansFichier = 0 ;
2021-12-20 17:10:35 +01:00
while ( curseur ! = NULL ) { // parcours de la liste
2021-12-24 15:45:43 +01:00
if ( curseur - > lettre ! = ' \0 ' ) nombreLettresDansFichier + + ; // +1 au compteur si c'est bien une lettre
2021-12-20 17:10:35 +01:00
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)
2021-12-24 00:20:57 +01:00
printf ( " Impossible d'allouer de la mémoire supplémentaire (listeVersArbre). \n " ) ; // gestion de l'erreur
2021-12-20 17:10:35 +01:00
exit ( 1 ) ;
}
2021-12-18 13:51:10 +01:00
2021-12-20 17:10:35 +01:00
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 ;
2021-12-24 15:45:43 +01:00
if ( curseur - > suivant - > lettre ! = ' \0 ' ) nombreLettresDansFichier + + ; // +1 si le suivant est aussi une lettre (membre droit)
2021-12-20 17:10:35 +01:00
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 ;
}
}
2021-12-24 15:45:43 +01:00
return nombreLettresDansFichier ;
2021-12-18 13:51:10 +01:00
}
2021-12-24 19:12:09 +01:00
void assignationCode ( Arbre arbre , int codeActuel , int longueur , Entete * enteteListe , int * i , int * longueurTotale ) {
2021-12-24 00:15:30 +01:00
if ( arbre - > lettre ! = ' \0 ' ) { // si c'est une lettre qu'on regarde
2021-12-24 19:12:09 +01:00
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)
2021-12-24 00:15:30 +01:00
* 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
2021-12-24 19:12:09 +01:00
assignationCode ( arbre - > gauche , codeActuel , longueur , enteteListe , i , longueurTotale ) ; // appel récursif arbre de gauche
2021-12-24 00:15:30 +01:00
codeActuel | = 1 ; // copie de bit si nécessaire (porte ou)
2021-12-24 19:12:09 +01:00
assignationCode ( arbre - > droite , codeActuel , longueur , enteteListe , i , longueurTotale ) ; // appel récursif arbre de droite
2021-12-23 20:02:20 +01:00
}
}
2021-12-24 00:24:33 +01:00
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
}
2021-12-24 15:45:43 +01:00
Entete * arbreVersListe ( Arbre arbre , int taille , int * tailleTotale ) {
2021-12-24 19:12:09 +01:00
Entete * enteteListe ;
if ( ( enteteListe = ( Entete * ) malloc ( taille * sizeof ( Entete ) ) ) = = NULL ) { // on alloue la liste qui va contenir nos caractères
2021-12-24 00:19:57 +01:00
printf ( " Impossible d'allouer de la mémoire supplémentaire (arbreVersListe). \n " ) ; // gestion de l'erreur
exit ( 1 ) ;
}
2021-12-24 00:15:30 +01:00
int i = 0 ; // initialisation de `i` au début car `assignationCode` est récursif
2021-12-24 19:12:09 +01:00
assignationCode ( arbre , ' \0 ' , 0 , enteteListe , & i , tailleTotale ) ; // on commence avec une racine nulle et une taille de 0
2021-12-24 00:24:33 +01:00
freeArbre ( arbre ) ;
2021-12-24 00:15:30 +01:00
2021-12-24 19:12:09 +01:00
return enteteListe ;
2021-12-24 00:15:30 +01:00
}
2021-12-24 15:45:43 +01:00
Entete * fichierVersListe ( FILE * fichier , int * nombreLettresDansFichier , int * tailleTotale ) {
2021-12-24 20:31:53 +01:00
char lettre = ' a ' ; // initalisation pour éviter un warning
2021-12-24 15:45:43 +01:00
Liste liste = NULL ;
while ( lettre ! = EOF ) {
lettre = fgetc ( fichier ) ;
ajouterLettre ( & liste , lettre ) ;
}
rewind ( fichier ) ;
trierListe ( & liste ) ;
* nombreLettresDansFichier = listeVersArbre ( & liste ) ;
return arbreVersListe ( liste , * nombreLettresDansFichier , tailleTotale ) ;
}
2021-12-23 20:02:20 +01:00
void compression ( FILE * entree , FILE * sortie ) {
2021-12-24 15:45:43 +01:00
int nombreLettresDansFichier ; // initialisé par `listeVersArbre`
2021-12-26 15:23:38 +01:00
int tailleTotale = 0 ; // taille totale en bit du fichier en sortie
2021-12-24 19:12:09 +01:00
Entete * enteteListe = fichierVersListe ( entree , & nombreLettresDansFichier , & tailleTotale ) ;
2021-12-24 15:45:43 +01:00
// On écrit l'entête du fichier avec la table complète des correspondances
2021-12-24 19:12:09 +01:00
enteteVersFichier ( enteteListe , nombreLettresDansFichier , tailleTotale , sortie ) ;
2021-12-24 15:45:43 +01:00
// On écrit les données huffman-isée dans le fichier
2021-12-24 19:12:09 +01:00
huffmanVersFichier ( entree , sortie , enteteListe , nombreLettresDansFichier ) ;
2021-12-24 19:22:30 +01:00
free ( enteteListe ) ; // libération de la liste car utilisation du malloc dans `arbreVersListe`
}
void enteteVersFichier ( Entete * enteteListe , int nombreLettresDansFichier , int longueurTotale , FILE * fichier ) {
2021-12-24 19:28:42 +01:00
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é
2021-12-24 19:33:11 +01:00
fwrite ( & ( enteteListe [ i ] . lettre ) , sizeof ( char ) , 1 , fichier ) ;
fwrite ( & ( enteteListe [ i ] . code ) , sizeof ( int ) , 1 , fichier ) ;
fwrite ( & ( enteteListe [ i ] . longueur ) , sizeof ( int ) , 1 , fichier ) ;
2021-12-24 19:28:42 +01:00
}
2021-12-24 19:22:30 +01:00
}
void huffmanVersFichier ( FILE * entree , FILE * sortie , Entete * enteteListe , int nombreLettresDansFichier ) {
2021-12-27 11:08:13 +01:00
int buffer = 0 , tailleBufferActuelle = 0 , sizeOfInt = sizeof ( int ) * 8 ; // *8 car quelque fois certains caractères sont mal encodés (en plus ça réduit la taille du fichier final)
2021-12-25 13:44:40 +01:00
char lettre = ' a ' ; // initialisation pour éviter un warning
while ( lettre ! = EOF ) { // on parcours le fichier
2021-12-24 20:31:53 +01:00
lettre = fgetc ( entree ) ;
2021-12-25 11:59:03 +01:00
Entete entete = recuperationLettre ( lettre , enteteListe , nombreLettresDansFichier ) ; // rappel: entete représente une liste de caractères
if ( tailleBufferActuelle + entete . longueur > = sizeOfInt ) { // écriture dans le fichier
2021-12-24 20:31:53 +01:00
// Modification du buffer
2021-12-25 11:59:03 +01:00
int aAjouter = sizeOfInt - tailleBufferActuelle ;
2021-12-25 13:44:40 +01:00
if ( aAjouter < 32 )
buffer < < = aAjouter ; // décalage vers la gauche
else { // évite un overflow (https://gcc.gnu.org/onlinedocs/gcc/Static-Analyzer-Options.html#index-Wanalyzer-shift-count-overflow)
printf ( " Déplacement de bits trop important (aAjouter >=32 [=%d]) " , aAjouter ) ;
exit ( 1 ) ;
}
2021-12-24 20:31:53 +01:00
// Ajout de la lettre
2021-12-25 11:59:03 +01:00
int tmp = entete . code > > ( entete . longueur - aAjouter ) ;
entete . longueur - = aAjouter ;
2021-12-24 20:31:53 +01:00
2021-12-25 11:59:03 +01:00
// Écriture dans le fichier ici
2021-12-24 20:31:53 +01:00
buffer | = tmp ;
fwrite ( & buffer , sizeof ( int ) , 1 , sortie ) ;
2021-12-25 11:59:03 +01:00
buffer = 0 , tailleBufferActuelle = 0 ; // reset du buffer
2021-12-24 20:31:53 +01:00
}
2021-12-25 13:44:40 +01:00
// Mise à jour du buffer
tailleBufferActuelle + = entete . longueur ;
buffer < < = entete . longueur ; // décalage vers la gauche
buffer | = entete . code ;
2021-12-24 20:31:53 +01:00
}
2021-12-25 11:59:03 +01:00
if ( tailleBufferActuelle > 0 ) {
buffer < < = sizeOfInt - tailleBufferActuelle ; // décalage vers la gauche
2021-12-24 20:31:53 +01:00
fwrite ( & buffer , sizeof ( int ) , 1 , sortie ) ;
}
2021-12-18 13:51:10 +01:00
}
2021-12-25 11:59:03 +01:00
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 ) ;
}
2021-12-26 15:23:38 +01:00
Arbre lectureDonnees ( FILE * fichier , int * tailleTotale ) {
2021-12-27 11:08:13 +01:00
int nombreLettresDansFichier , lecture = 0 ; // bytes lues
2021-12-26 15:40:22 +01:00
lecture + = fread ( & nombreLettresDansFichier , sizeof ( int ) , 1 , fichier ) ;
lecture + = fread ( tailleTotale , sizeof ( int ) , 1 , fichier ) ;
2021-12-26 15:23:38 +01:00
Entete * entete ;
if ( ( entete = ( Entete * ) malloc ( nombreLettresDansFichier * sizeof ( Entete ) ) ) = = NULL ) { // on alloue de la mémoire pour l'entête
printf ( " Impossible d'allouer de la mémoire supplémentaire (lectureDonnees). \n " ) ; // gestion de l'erreur
exit ( 1 ) ;
}
Arbre arbre = allouerCellule ( ' \0 ' ) ;
for ( int i = 0 ; i < nombreLettresDansFichier ; i + + ) {
2021-12-26 16:05:24 +01:00
// Lecture de l'entête
2021-12-26 15:40:22 +01:00
lecture + = fread ( & ( entete [ i ] . lettre ) , sizeof ( char ) , 1 , fichier ) ;
lecture + = fread ( & ( entete [ i ] . code ) , sizeof ( int ) , 1 , fichier ) ;
lecture + = fread ( & ( entete [ i ] . longueur ) , sizeof ( int ) , 1 , fichier ) ;
2021-12-26 16:05:24 +01:00
// Ajout de la lettre à l'arbre
Cellule * curseur = arbre ;
2021-12-27 11:08:13 +01:00
int mask = 1 ; // mask est le masque de bit appliqué pour savoir si on doit aller a la gauche ou a la droite de l'arbre
2021-12-26 16:05:24 +01:00
mask < < = entete [ i ] . longueur - 1 ; // décalage vers la gauche de la longueur de la lettre - 1
for ( int j = 0 ; j < entete [ i ] . longueur ; j + + ) {
2021-12-27 11:55:55 +01:00
int gaucheOuDroite = entete [ i ] . code & mask ;
2021-12-26 16:05:24 +01:00
entete [ i ] . code < < = 1 ; // décalage de 1 vers la gauche pour éviter la segfault
2021-12-27 11:55:55 +01:00
if ( gaucheOuDroite ) { // droite
2021-12-26 16:05:24 +01:00
if ( curseur - > droite = = NULL ) curseur - > droite = allouerCellule ( ' \0 ' ) ; // on remplace NULL par le caractère correspondant
curseur = curseur - > droite ;
2021-12-27 11:08:13 +01:00
} else { // gauche
if ( curseur - > gauche = = NULL ) curseur - > gauche = allouerCellule ( ' \0 ' ) ; // on remplace NULL par le caractère correspondant
curseur = curseur - > gauche ;
2021-12-26 16:05:24 +01:00
}
}
2021-12-27 11:41:26 +01:00
curseur - > lettre = entete [ i ] . lettre ; // on assigne la bonne lettre correspondante
2021-12-26 15:23:38 +01:00
}
free ( entete ) ; // on libère l'entête car on en a plus besoin
2021-12-27 11:08:13 +01:00
printf ( " %d bytes lus pour reconstruire l'arbre. \n " , lecture ) ;
2021-12-26 15:23:38 +01:00
return arbre ;
2021-12-25 14:03:38 +01:00
}
2021-12-26 15:23:38 +01:00
void huffmanDepuisFichier ( FILE * entree , FILE * sortie , Arbre arbre , int tailleTotale ) {
2021-12-27 11:08:13 +01:00
int mask = 1 , buffer , bitsLus = 0 , lecture = 0 , sizeOfInt = sizeof ( int ) * 8 ;
int bitsMax = tailleTotale / sizeOfInt ;
if ( tailleTotale % sizeOfInt ! = 0 ) bitsMax + + ; // parce que c'est un arbre binaire
mask < < = sizeOfInt - 1 ; // on récupère le bon bit
Cellule * curseur = arbre ;
for ( int i = 0 ; i < bitsMax ; i + + ) { // on parcours le fichier
lecture + = fread ( & buffer , sizeof ( int ) , 1 , entree ) ;
for ( int j = 0 ; bitsLus < tailleTotale & & j < sizeOfInt ; j + + ) {
if ( curseur - > lettre ! = ' \0 ' ) { // update de l'arbre si on est sur une 'mini-racine'
fprintf ( sortie , " %c " , curseur - > lettre ) ; // on ajoute le caractères dans le fichier de sortie
curseur = arbre ;
}
if ( mask & buffer ) curseur = curseur - > droite ; // droite
else curseur = curseur - > gauche ; // gauche
bitsLus + + ; // on incrémente bitsLus de 1
buffer < < = 1 ; // update du buffer
}
}
printf ( " %d bytes lus pour reconstruire le fichier. \n " , lecture ) ;
2021-12-25 14:03:38 +01:00
}
2021-12-23 20:02:20 +01:00
void decompression ( FILE * entree , FILE * sortie ) {
2021-12-26 15:23:38 +01:00
int tailleTotale ; // taille totale en bit du fichier en entrée
Arbre arbre = lectureDonnees ( entree , & tailleTotale ) ;
huffmanDepuisFichier ( entree , sortie , arbre , tailleTotale ) ;
2021-12-25 14:03:38 +01:00
freeArbre ( arbre ) ; // on libère l'arbre
2021-12-18 13:51:10 +01:00
}