home *** CD-ROM | disk | FTP | other *** search
- #include "lzwdico.h"
- #include <assert.h>
- #include <iostream.h>
-
- // Code spécial signifiant échec d'une recherche de caractère
- const LZWEntree::code LZWEntree::RIEN = 256 ;
- // (cette sémantique du RIEN est courante : lorsque vous échouez
- // à trouver un pointeur, vous renvoyez 0 ; lorsque EXCEL échoue
- // à trouver une valeur, il renvoie #N/A ; lorsque vous cherchez
- // un index positif vous pouvez renvoyer -1 s'il est absent, etc ;
- // Moi j'aime bien appeler les choses par leur nom : RIEN !!)
- //
- // Je rappelle que les codes 0 à 255 sont réservés pour les carac-
- // tères ordinaires trouvés dans le source à comprimer.
-
- // Codes spéciaux émis à destination du décompresseur
- const LZWEntree::code LZWEntree::FIN_FICHIER = 257 ;
- const LZWEntree::code LZWEntree::ALLONGER_CODE = 258 ;
- const LZWEntree::code LZWEntree::IGNORER_DICO = 259 ; // ne sert pas
- const LZWEntree::code LZWEntree::VIDER_DICO = 260 ;
-
- // Divers paramètres pour assurer l'évolutivité du code
- const LZWEntree::code LZWDico::CODE_DEPART = 261 ; // premier code libre
- const unsigned int LZWDico::NOMBRE_BITS_MIN = 9 ; // longueur min de code
- const unsigned int LZWDico::NOMBRE_BITS_MAX = 16 ; // lg max absolue de code
- // (tient encore dans un int)
-
- LZWEntree::code LZWDico::premier( unsigned int nbits )
- //
- // Renvoie un nombre premier dépendant de la longueur actuelle du
- // code, passée en argument. En effet, l'accès au dico comporte un
- // calcul de hachage. Or, pour obtenir des performances correctes,
- // la taille d'une hashtable doit être :
- // - un nombre premier ;
- // - sensiblement supérieure au nombre maximum d'éléments à stocker.
- //
- // Les premiers ci-dessous sont 23% supérieurs à la puissance de 2
- // correspondant au nombre de bits, ce qui est juste suffisant selon
- // mes constatations. (par exemple, pour 13 bits (max 8192 éléments),
- // la valeur choisie de 10103 conduit à un temps 5 fois meilleur que
- // 9851, alors qu'aller au-delà n'apporte plus aucune amélioration).
- // (Nelson préconise une marge de 20%, moi j'ai trouvé qu'il vaut en-
- // la peine d'aller à 23%, mais pas au-delà ; je suppose que cela peut
- // dépendre des environnements : mémoire, processeur, etc).
- {
- switch( nbits )
- {
- case 9 : return 641 ;
- case 10 : return 1277 ;
- case 11 : return 2531 ;
- case 12 : return 5051 ; // Nelson préconisait 5021
- case 13 : return 10103 ;
- case 14 : return 20219 ;
- case 15 : return 40423;
- }
- assert( 0 ) ; // fera tilt si nbits < 9 ou > 15
- return 0 ; // jamais exécuté, mais calme le (stupide) compilateur
- }
-
-
- // Constructeur du dictionnaire LZW
- // nombre_bits_final est la longueur de code au delà de laquelle
- // on souhaite vider le dictionnaire (tout le monde n'a pas 16MO
- // de mémoire !)
- // CODE_LIMITE est le dernier code pouvant être émis avec la lon-
- // gueur de code actuelle
- // CODE_FINAL est le plus haut code que nous pourrons émettre avec
- // la longueur de code nombre_bits_final (donc juste avant vidage)
- // Il est crucial de faire un minimum de calculs à chaque octet lu,
- // c'est pourquoi l'on stocke de nombreux paramètres (quitte à les
- // remettre à jour dès que nécessaire).
- //
- // maxval est une fonction renvoyant le plus haut code possible pour
- // une longueur donnée (par exemple 511 pour 9 bits).
- //
- LZWDico::LZWDico( unsigned int nombre_bits_final )
- : NOMBRE_BITS_ACTUEL( NOMBRE_BITS_MIN ),
- TAILLE_DICO( premier( nombre_bits_final ) ),
- DICO( new LZWEntree[ premier( nombre_bits_final ) ] ),
- CODE_FINAL( maxval( nombre_bits_final ) ),
- CODE_LIMITE( maxval( NOMBRE_BITS_MIN ) ),
- CODE_LIBRE_SUIVANT( CODE_DEPART )
- {
- assert( nombre_bits_final >= NOMBRE_BITS_MIN
- && nombre_bits_final <= NOMBRE_BITS_MAX
- && DICO != 0 ) ;
- }
-
-
- LZWDico::~LZWDico()
- {
- delete [] DICO ;
- }
-
-
- void LZWDico::vider()
- //
- // Rétablit le dico dans son état de départ (en mettant des RIEN dans
- // ses entrées, en mettant la longueur de code à celle de départ, etc)
- {
- NOMBRE_BITS_ACTUEL = NOMBRE_BITS_MIN ;
- CODE_LIBRE_SUIVANT = CODE_DEPART ;
- CODE_LIMITE = maxval( NOMBRE_BITS_MIN ) ;
- for( LZWEntree::code i = 0 ; i < TAILLE_DICO ; ++i )
- DICO[ i ].ajoute( LZWEntree::RIEN, LZWEntree::RIEN ) ;
- }
-
-
- LZWEntree::code LZWDico::index ( LZWEntree::code cparent, unsigned char carfils ) const
- //
- // Cette fonction reçoit en argument :
- // - le code (l'index dans le dico) cparent, dont on veut trouver le fils ;
- // - le caractère qui étiquette le fils que l'on cherche.
- // Elle doit renvoyer l'index du noeud fils dans le dictionnaire. Ce n'est
- // pas trivial car il n'y a pas de pointeur dans le sens parent vers fils.
- // Le principe est qu'un noeud fils est rangé à un index calculé par hacha-
- // ge à partir du code du parent et du caractère qui étiquette le fils.
- {
- // Ceci est la fonction de hachage donnant l'index présumé du fils
- LZWEntree::code ix = ( ( ( LZWEntree::code )carfils ) << ( nbits() - 8 ) ) ^ cparent ;
-
- // Il reste à résoudre les ex-aequos à cet index (collisions)
- // Pour cela on parcourt circulairement le dictionnaire, jusqu'à
- // trouver un noeud vide (on renvoie RIEN), ou un noeud dont les
- // éléments code et car sont conformes à ceux en argument (réso-
- // lution réussie de la collision). On renvoie alors l'index.
- LZWEntree::code dec = ix ? tailleDico() - ix : 1 ;
- for( ; ; ix = ( ix >= dec ) ? ix - dec : ix + tailleDico() - dec )
- {
- if( DICO[ ix ].leCode() == LZWEntree::RIEN )
- break ;
- if( DICO[ ix ].leParent() == cparent && DICO[ ix ].leCar() == carfils )
- break ;
- }
- return ix ;
- }
-
-
- #ifdef DEPANNAGE
- void LZWDico::lister() const
- //
- // J'avoue humblement que cela m'a servi durant la mise au point...
- {
- cout << endl ;
- cout << "taille dico : " << TAILLE_DICO
- << endl ;
- cout << "nbre bits :"
- << "\tmin " << NOMBRE_BITS_MIN
- << "\tmax " << NOMBRE_BITS_MAX
- << "\tactuel " << NOMBRE_BITS_ACTUEL
- << endl ;
- cout << "codes :"
- << "\tdépart " << CODE_DEPART
- << "\tlibre " << CODE_LIBRE_SUIVANT
- << "\tlimite " << CODE_LIMITE
- << "\tfinal " << CODE_FINAL
- << endl ;
- }
- #endif
-