home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
DP Tool Club 21
/
CD_ASCQ_21_040595.iso
/
dos
/
prg
/
c
/
complzw
/
lzwdico.cpp
< prev
next >
Wrap
C/C++ Source or Header
|
1995-02-02
|
6KB
|
160 lines
#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