Section courante

A propos

Section administrative du site

 Langage  Elément  Tutoriel  Aide 
ABAP/4
Ada
Assembleur
Assembly & bytecode
ASP (Active Server Pages)
Basic
C
C++
C# (C Sharp)
Cobol
ColdFusion
Fortran
HTML
Java
JavaScript
LISP
Logo
LotusScript
Oberon
Pascal
Perl
PHP
PL/1
Prolog
Python
Rebol
REXX
Ruby
SAS
NoSQL
SQL
Swift
X++ (Axapta)
GNAT
SMALLAda
VHDL
Assembleur 370
Assembleur 1802
Assembleur 4004
Assembleur 6502
Assembleur 6800
Assembleur 68000
Assembleur 8080 et 8085
Assembleur 8089
Assembleur 80x86
Assembleur AGC4
Assembleur ARM
Assembleur DPS 8000
Assembleur i860
Assembleur Itanium
Assembleur MIPS
Assembleur PDP-11
Assembleur PowerPC
Assembleur RISC-V
Assembleur SPARC
Assembleur SuperH
Assembleur UNIVAC I
Assembleur VAX
Assembleur Z80
Assembleur Z8000
Assembleur z/Architecture
ASSEMBLER/MONITOR 64
Micol Assembler
GFA Assembler
A86
MASM (Macro Assembler)
TASM (Turbo Assembler)
CIL
Jasmin
LLVM
MSIL
Parrot
P-Code (PCode)
SWEET16
G-Pascal
ASP 1.0
ASP 2.0
ASP 3.0
ASP.NET
ASP.NET Core
ABasiC (Amiga)
Adam SmartBASIC
Altair BASIC
AmigaBASIC (Amiga)
AMOS Basic (Amiga)
Atari Basic (Atari 400, 600 XL, 800, 800XL)
Basic Apple II (Integer BASIC/APPLESOFT)
Basic Commodore 64 (CBM-BASIC)
Basic Commodore 128 (BASIC 7.0)
Basic Commodore VIC-20 (CBM-BASIC 2.0)
Basic Coco 1 (Color Basic)
Basic Coco 2 (Extended Color Basic)
Basic Coco 3 (Extended Color Basic 2.0)
BASICA (PC DOS)
Basic Pro
BBC BASIC
Blitz BASIC (Amiga)
DarkBASIC
Dartmouth BASIC
GFA-Basic (Atari ST/Amiga)
GWBASIC (MS-DOS)
Liberty BASIC
Locomotive BASIC (Amstrad CPC)
MSX-Basic
Omikron Basic (Atari ST)
Oric Extended Basic
Power Basic
Quick Basic/QBasic (MS-DOS)
Sinclair BASIC (ZX80, ZX81, ZX Spectrum)
ST BASIC (Atari ST)
Turbo Basic
Vintage BASIC
VBScript
Visual Basic (VB)
Visual Basic .NET (VB .NET)
Visual Basic pour DOS
Yabasic
BeckerBASIC
SIMONS' BASIC
Basic09 d'OS-9
Disk Extended Color Basic
Basic09 d'OS-9
Disk Extended Color Basic
Access
Excel
Visual Basic pour Windows
Visual Basic .NET pour Windows
C Shell Unix (csh)
C pour Amiga
C pour Atari ST
C pour DOS
C pour Falcon030
C pour GEMDOS (Atari ST)
C pour Linux
C pour PowerTV OS
C pour OS/2
C pour Unix
C pour Windows
Aztec C
CoCo-C
GNU C
HiSoft C
IBM C/2
Introl-C
Lattice C
Microsoft C
MinGW C
MSX-C
Open Watcom C
OS-9 C Compiler
Pure C
Quick C
Turbo C
HiSoft C for Atari ST
HiSoft C for CP/M (Amstrad CPC)
C++ pour OS/2
C++ pour Windows
Borland C++
C++Builder
IBM VisualAge C++
Intel C++
MinGW C++
Open Watcom C++
Symantec C++
Turbo C++
Visual C++
Visual C++ .NET
Watcom C++
Zortech C++
C# (C Sharp) pour Windows
Apple III Cobol
Microsoft Cobol
BlueDragon
Lucee
OpenBD
Railo
Smith Project
Microsoft Fortran
WATFOR-77
CSS
FBML
Open Graph
SVG
XML
XSL/XSLT
LESS
SASS
GCJ (GNU)
JSP
Jython
Visual J++
Node.js
TypeScript
AutoLISP
ACSLogo
LotusScript pour Windows
Amiga Oberon
Oberon .NET
Apple Pascal
Delphi/Kylix/Lazarus
Free Pascal
GNU Pascal
HighSpeed Pascal
IBM Personal Computer Pascal
Lisa Pascal
Maxon Pascal
MPW Pascal
OS-9 Pascal
OSS Personal Pascal
Pascal-86
Pascal du Cray Research
Pascal/VS
Pascal-XT
PURE Pascal
QuickPascal
RemObjets Chrome
Sun Pascal
THINK Pascal
Tiny Pascal (TRS-80)
Turbo Pascal
UCSD Pascal
VAX Pascal
Virtual Pascal
Turbo Pascal for CP/M-80
Turbo Pascal for DOS
Turbo Pascal for Macintosh
Turbo Pascal for Windows
CodeIgniter (Cadre d'application)
Drupal (Projet)
Joomla! (Projet)
Phalanger (PHP .NET)
phpBB (Projet)
Smarty (balise)
Twig (balise)
Symfony (Cadre d'application)
WordPress (Projet)
Zend (Cadre d'application)
PL360
PL/M-80
PL/M-86
Turbo Prolog
CPython
IronPython
Jython
PyPy
AREXX
Regina REXX
JMP
Btrieve
Cassandra
Clipper
CouchDB
dBASE
Hbase
Hypertable
MongoDB
Redis
Access
BigQuery
DB2
H2
Interbase
MySQL
Oracle
PostgreSQL
SAP HANA
SQL Server
Sybase
U-SQL
Introduction
Les constantes
Les structures de données
Les fonctions de base
Les fonctions avancées
Les fonctions utilitaires
Les fonctions d'accès aux fichiers gzip
Les fonctions de somme de contrôle
Les fonctions non documentées
Les premiers pas
Préface
Notes légales
Dictionnaire
Recherche

Les premiers pas

La base de zlib repose les fonctions deflate() et inflate(). Les utilisateurs se demandent quand ils doivent fournir plus d'entrées, quand ils doivent utiliser plus de sorties, que faire avec une Z_BUF_ERROR, comment s'assurer que le processus se termine correctement,... Voici un exemple annoté en C de routines simples pour compresser et décompresser d'un fichier d'entrée vers un fichier de sortie en utilisant respectivement deflate() et inflate(). Les annotations sont intercalées entre les lignes du code. Veuillez donc lire entre les lignes. Nous espérons que cela vous aidera à comprendre certaines des subtilités de zlib.

Sans plus attendre, voici le programme zpipe.c traduit en français :

  1. /* zpipe.c: exemple d'utilisation correcte des fonctions inflate() et deflate() de zlib
  2.    Non protégé par le droit d'auteur - fourni au domaine public
  3.    Version 1.4, 11 décembre 2005, Mark Adler */
  4.  
  5. /* Historique des versions :
  6.    1.0, 30 octobre 2004, Première version
  7.    1.1, 8 novembre 2004, Ajout d'un cast void pour les valeurs de retour inutilisées. Utilisation de l'instruction switch pour les valeurs de retour inflate()
  8.    1.2, 9 novembre 2004, ajout d'assertions pour documenter les garanties zlib
  9.    1.3, 6 avril 2005, Suppression d'une assertion incorrecte dans inf()
  10.    1.4, 11 décembre 2005, Ajout d'un hack pour éviter les conversions de fin de ligne MSDOS
  11.    Éviter certains avertissements du compilateur pour les tampons d'entrée et de sortie
  12.  */

Nous incluons maintenant les fichiers d'entête pour les définitions requises. Depuis stdio.h, nous utilisons fopen(), fread(), fwrite(), feof(), ferror() et fclose() pour les entrées/sorties de fichiers et fputs() pour les messages d'erreur. Depuis string.h, nous utilisons strcmp() pour le traitement des paramètres de la ligne de commande. Depuis assert.h, nous utilisons la macro assert(). Depuis zlib.h, nous utilisons les fonctions de compression de base deflateInit(), deflate() et deflateEnd(), ainsi que les fonctions de décompression de base inflateInit(), inflate() et inflateEnd().

  1. #include <stdio.h>
  2. #include <string.h>
  3. #include <assert.h>
  4. #include "zlib.h"

Il s'agit d'un hack malicieux nécessaire pour éviter la corruption des données d'entrée et de sortie sur les systèmes Windows/MS-DOS. Sans cela, ces systèmes supposeraient que les fichiers d'entrée et de sortie sont du texte et essaieraient de convertir les caractères de fin de ligne d'un standard à un autre. Cela corrompra les données binaires et, en particulier, rendrait les données compressées inutilisables. Cela définit l'entrée et la sortie en binaire, ce qui supprime les conversions de fin de ligne. SET_BINARY_MODE() sera utilisé plus tard sur stdin et stdout, au début de main() :

  1. #if defined(MSDOS) || defined(OS2) || defined(WIN32) || defined(__CYGWIN__)
  2. #  include <fcntl.h>
  3. #  include <io.h>
  4. #  define SET_BINARY_MODE(file) _setmode(_fileno(file), O_BINARY)
  5. #else
  6. #  define SET_BINARY_MODE(file)
  7. #endif

CHUNK est simplement la taille du tampon pour alimenter et extraire les données des routines zlib. Des tailles de tampon plus grandes seraient plus efficaces, en particulier pour inflate(). Si la mémoire est disponible, des tailles de tampon de l'ordre de 128 Ko ou 256 Ko doivent être utilisées.

  1. #define CHUNK 16384

La routine def() compresse les données d'un fichier d'entrée vers un fichier de sortie. Les données de sortie seront au format zlib, étant différent des formats gzip ou zip. Le format zlib possède un très petit entête de seulement deux octets pour l'identifier comme un flux zlib et pour fournir des informations de décodage, ainsi qu'une fin de bande de quatre octets avec une valeur de contrôle rapide pour vérifier l'intégrité des données non compressées après le décodage.

  1. /* Compresser du fichier source au fichier de destination jusqu'à la fin du fichier source. def() renvoie Z_OK en cas de succès, 
  2.    Z_MEM_ERROR si la mémoire n'a pas pu être allouée pour le traitement, Z_STREAM_ERROR si un niveau de compression non valide est 
  3.    fourni, Z_VERSION_ERROR si la version de zlib.h et la version de la bibliothèque liée ne correspondent pas, ou Z_ERRNO s'il y a 
  4.    une erreur de lecture ou d'écriture des fichiers. */
  5. int def(FILE *source, FILE *dest, int level)
  6. {

Voici les variables locales pour def(). ret sera utilisé pour les codes de retour zlib. flush gardera une trace de l'état de vidage actuel pour deflate(), étant soit aucun vidage, soit un vidage complet une fois la fin du fichier d'entrée atteinte. have est la quantité de données renvoyées par deflate(). La structure strm est utilisée pour transmettre des informations vers et depuis les routines zlib, et pour maintenir l'état deflate(). in et out sont les tampons d'entrée et de sortie pour deflate().

  1. int ret, flush;
  2. unsigned have;
  3. z_stream strm;
  4. unsigned char in[CHUNK];
  5. unsigned char out[CHUNK];

La première chose que nous faisons est d'initialiser l'état zlib pour la compression en utilisant deflateInit(). Cela doit être fait avant la première utilisation de deflate(). Les champs zalloc, zfree et opaque dans la structure strm doivent être initialisés avant d'appeler deflateInit(). Ici, ils sont définis sur la constante zlib Z_NULL pour demander que zlib utilise les routines d'allocation de mémoire par défaut. Une application peut également choisir de fournir ici des routines d'allocation de mémoire personnalisées. deflateInit() allouera de l'ordre de 256 Ko pour l'état interne.

deflateInit() est appelé avec un pointeur vers la structure à initialiser et le niveau de compression, étant un entier compris entre -1 et 9. Des niveaux de compression inférieurs entraînent une exécution plus rapide, mais une compression moindre. Des niveaux plus élevés entraînent une compression plus importante, mais une exécution plus lente. La constante zlib Z_DEFAULT_COMPRESSION, égale à -1, offre un bon compromis entre compression et vitesse et est équivalente au niveau 6. Le niveau 0 ne fait en réalité aucune compression et étend en fait légèrement les données pour produire le format zlib (il ne s'agit pas d'une copie octet par octet de l'entrée). Les applications zlib plus avancées peuvent ici utiliser deflateInit2(). Une telle application peut vouloir réduire la quantité de mémoire étant utilisée, à un certain prix en termes de compression. Ou elle peut avoir besoin de demander un entête et une bande-annonce gzip au lieu d'un entête et d'une bande-annonce zlib, ou un codage brut sans entête ni bande-annonce du tout.

Nous devons vérifier la valeur de retour de deflateInit() par rapport à la constante zlib Z_OK pour nous assurer qu'elle a pu allouer de la mémoire pour l'état interne et que les arguments fournis étaient valides. deflateInit() vérifiera également que la version de zlib d'où provient le fichier zlib.h correspond à la version de zlib réellement liée au programme. Ceci est particulièrement important pour les environnements dans lesquels zlib est une bibliothèque partagée.

Notez qu'une application peut initialiser plusieurs flux zlib indépendants, qui peuvent fonctionner en parallèle. Les informations d'état conservées dans la structure permettent aux routines zlib d'être réentrantes.

  1. /* allouer un état deflate  */
  2. strm.zalloc = Z_NULL;
  3. strm.zfree = Z_NULL;
  4. strm.opaque = Z_NULL;
  5. ret = deflateInit(&strm, level);
  6. if (ret != Z_OK) return ret;

Maintenant que les choses sérieuses sont faites, nous pouvons passer aux choses sérieuses. La boucle externe lit tout le fichier d'entrée et sort en bas de la boucle une fois la fin du fichier atteinte. Cette boucle contient le seul appel de deflate(). Nous devons donc nous assurer que toutes les données d'entrée ont été traitées et que toutes les données de sortie ont été générées et consommées avant de sortir de la boucle en bas.

  1. /* compresser jusqu'à la fin du fichier */
  2. do {

Nous commençons par lire les données du fichier d'entrée. Le nombre d'octets lus est placé directement dans avail_in, et un pointeur vers ces octets est placé dans next_in. Nous vérifions également si la fin du fichier sur l'entrée a été atteinte en utilisant feof(). Si nous sommes à la fin du fichier, flush est défini sur la constante zlib Z_FINISH, étant ensuite passée à deflate() pour indiquer qu'il s'agit du dernier bloc de données d'entrée à compresser. Si nous ne sommes pas encore à la fin de l'entrée, la constante zlib Z_NO_FLUSH sera passée à deflate pour indiquer que nous sommes toujours au milieu des données non compressées.

S'il y a une erreur lors de la lecture du fichier d'entrée, le processus est interrompu et deflateEnd() est appelé pour libérer l'état zlib alloué avant de renvoyer l'erreur. Nous ne voudrions pas de fuite de mémoire, n'est-ce pas ? deflateEnd() peut être appelé à tout moment après l'initialisation de l'état. Une fois cela fait, deflateInit() (ou deflateInit2()) devra être appelé pour démarrer un nouveau processus de compression. Il n'y a aucun intérêt ici à vérifier le code de retour deflateEnd(). La désallocation ne peut pas échouer.

  1. strm.avail_in = fread(in, 1, CHUNK, source);
  2. if (ferror(source)) {
  3.     (void)deflateEnd(&strm);
  4.     return Z_ERRNO;
  5. }
  6. flush = feof(source) ? Z_FINISH : Z_NO_FLUSH;
  7. strm.next_in = in;

La boucle interne transmet notre bloc de données d'entrée à deflate(), puis continue d'appeler deflate() jusqu'à ce qu'elle ait fini de produire une sortie. Une fois qu'il n'y a plus de nouvelle sortie, deflate() est assuré d'avoir consommé toute l'entrée, c'est-à-dire que avail_in sera nul.

  1. /* exécuter deflate() sur l'entrée jusqu'à ce que le tampon de sortie ne soit pas plein, terminer la compression si toute la source a été lue */
  2. do {

L'espace de sortie est fourni à deflate() en définissant avail_out sur le nombre d'octets de sortie disponibles et next_out sur un pointeur vers cet espace.

  1. strm.avail_out = CHUNK;
  2. strm.next_out = out;

Nous appelons maintenant le moteur de compression lui-même, deflate(). Il prend autant d'octets avail_in dans next_in qu'il peut traiter et écrit autant d'octets avail_out dans next_out. Ces compteurs et pointeurs sont ensuite mis à jour après la consommation des données d'entrée et l'écriture des données de sortie. C'est la quantité d'espace de sortie disponible pouvant limiter la quantité d'entrée consommée. D'où la boucle interne pour s'assurer que toutes les entrées sont consommées en fournissant plus d'espace de sortie à chaque fois. Étant donné que avail_in et next_in sont mis à jour par deflate(), nous n'avons pas à les manipuler entre les appels deflate() jusqu'à ce qu'ils soient tous utilisés.

Les paramètres de deflate() sont un pointeur vers la structure strm contenant les informations d'entrée et de sortie et l'état interne du moteur de compression, et un paramètre indiquant si et comment vider les données vers la sortie. Normalement, deflate consommera plusieurs K octets de données d'entrée avant de produire une sortie (à l'exception de l'entête), afin d'accumuler des statistiques sur les données pour une compression optimale. Il émettra ensuite une salve de données compressées et continuera à consommer plus d'entrées avant la salve suivante. Finalement, il faut dire à deflate() de terminer le flux, de terminer la compression avec les données d'entrée fournies et d'écrire la valeur de contrôle de la remorque. deflate() continuera à compresser normalement tant que le paramètre de vidage est Z_NO_FLUSH. Une fois le paramètre Z_FINISH fourni, deflate() commencera à terminer le flux de sortie compressé. Cependant, en fonction de la quantité d'espace de sortie fourni, deflate() peut devoir être appelé plusieurs fois jusqu'à ce qu'il ait fourni le flux compressé complet, même après avoir consommé toutes les entrées. Le paramètre de vidage doit continuer à être Z_FINISH pour ces appels ultérieurs.

Il existe d'autres valeurs du paramètre de vidage étant utilisées dans des applications plus avancées. Vous pouvez forcer deflate() à produire une salve de sortie encodant toutes les données d'entrée fournies jusqu'à présent, même si cela n'aurait pas été le cas autrement, par exemple pour contrôler la latence des données sur une liaison avec des données compressées. Vous pouvez également demander à deflate() de faire cela ainsi que d'effacer tout historique jusqu'à ce point afin que ce qui suit puisse être décompressé indépendamment, par exemple pour les applications à accès aléatoire. Les deux requêtes dégraderont la compression d'un montant dépendant de la fréquence de ces requêtes.

deflate() a une valeur de retour pouvant indiquer des erreurs, mais nous ne la vérifions pas ici. Pourquoi pas ? Eh bien, il s'avère que deflate() ne peut pas faire d'erreur ici. Examinons les valeurs de retour de deflate() et éliminons-les une par une. Les valeurs possibles sont Z_OK, Z_STREAM_END, Z_STREAM_ERROR ou Z_BUF_ERROR. Z_OK signifie correcte et que donc tout c'est bien passé. Z_STREAM_END est également un équivalent de ok et sera renvoyé pour le dernier appel de deflate(). Ceci est déjà garanti en appelant deflate() avec Z_FINISH jusqu'à ce qu'il n'y ait plus de sortie. Z_STREAM_ERROR n'est possible que si le flux n'est pas initialisé correctement, mais nous l'avons initialisé correctement. Il n'y a aucun mal à vérifier ici Z_STREAM_ERROR, par exemple pour vérifier la possibilité qu'une autre partie de l'application ait par inadvertance écrasé la mémoire contenant l'état zlib. Z_BUF_ERROR sera expliqué plus loin, mais il suffit de dire que c'est simplement une indication que deflate() n'a pas pu consommer plus d'entrées ou produire plus de sorties. deflate() peut être appelé à nouveau avec plus d'espace de sortie ou plus d'entrées disponibles, ce qui sera le cas dans ce code.

  1. ret = deflate(&strm, flush);    /* aucune mauvaise valeur de retour */
  2. assert(ret != Z_STREAM_ERROR);  /* État non dévasté */

Nous calculons maintenant la quantité de sortie fournie par deflate() lors du dernier appel, ce qui correspond à la différence entre la quantité d'espace fournie avant l'appel et la quantité d'espace de sortie encore disponible après l'appel. Ensuite, ces données, le cas échéant, sont écrites dans le fichier de sortie. Nous pouvons ensuite réutiliser le tampon de sortie pour le prochain appel de deflate(). Encore une fois, s'il y a une erreur d'entrée/sortie de fichier, nous appelons deflateEnd() avant de revenir pour éviter une fuite de mémoire.

  1. have = CHUNK - strm.avail_out;
  2. if (fwrite(out, 1, have, dest) != have || ferror(dest)) {
  3.     (void)deflateEnd(&strm);
  4.     return Z_ERRNO;
  5. }

La boucle interne est répétée jusqu'à ce que le dernier appel deflate() ne parvienne pas à remplir le tampon de sortie fourni. Nous savons alors que deflate() a fait tout ce qu'il pouvait avec l'entrée fournie, et que toute cette entrée a été consommée. Nous pouvons alors sortir de cette boucle et réutiliser le tampon d'entrée.

La façon dont nous savons que deflate() n'a plus de sortie est de voir qu'il n'a pas rempli le tampon de sortie, laissant avail_out supérieur à zéro. Cependant, supposons que deflate() n'ait plus de sortie, mais qu'il ait juste rempli le tampon de sortie ! avail_out est nul, et nous ne pouvons pas dire que deflate() a fait tout ce qu'il pouvait. Pour autant que nous le sachions, deflate() a plus de sortie pour nous. Nous l'appelons donc à nouveau. Mais maintenant deflate() ne produit aucune sortie du tout, et avail_out reste inchangé en tant que CHUNK. Cet appel deflate() n'a rien pu faire, ni consommer l'entrée ni produire la sortie, et il renvoie donc Z_BUF_ERROR. Cependant, ce n'est pas du tout un problème. Maintenant, nous avons enfin l'indication souhaitée que deflate() est vraiment terminé, et nous sortons donc de la boucle interne pour fournir plus d'entrées à deflate().

Avec flush défini sur Z_FINISH, cet ensemble final d'appels deflate() complètera le flux de sortie. Une fois cela fait, les appels suivants de deflate() renverraient Z_STREAM_ERROR si le paramètre flush n'est pas Z_FINISH, et n'effectueraient plus de traitement jusqu'à ce que l'état soit réinitialisé.

Certaines applications de zlib ont deux boucles appelant deflate() au lieu de la seule boucle interne que nous avons ici. La première boucle appellerait sans vidage et alimenterait toutes les données à deflate(). La deuxième boucle appellerait deflate() sans plus de données et le paramètre Z_FINISH pour terminer le processus. Comme vous pouvez le voir dans cet exemple, cela peut être évité en gardant simplement une trace de l'état de vidage actuel.

  1. } while (strm.avail_out == 0);
  2. assert(strm.avail_in == 0);     /* toutes les entrées seront utilisées */

Nous vérifions maintenant si nous avons déjà traité l'intégralité du fichier d'entrée. Ces informations ont été enregistrées dans la variable flush, nous voyons donc si elle a été définie sur Z_FINISH. Si c'est le cas, nous avons terminé et nous sortons de la boucle externe. Nous sommes assurés d'obtenir Z_STREAM_END à partir du dernier appel deflate(), puisque nous l'avons exécuté jusqu'à ce que le dernier bloc d'entrée soit consommé et que toute la sortie soit générée.

  1.     /* terminé lorsque les dernières données du fichier ont été traitées */
  2. } while (flush != Z_FINISH);
  3. assert(ret == Z_STREAM_END);        /* le flux sera complet */

Le processus est terminé, mais nous devons encore désallouer l'état pour éviter une fuite de mémoire (ou plutôt une hémorragie de mémoire si vous ne l'avez pas fait). Ensuite, nous pouvons enfin revenir avec une valeur de retour satisfaisante.

  1.     /* nettoyer et retourner */
  2.     (void)deflateEnd(&strm);
  3.     return Z_OK;
  4. }

Nous faisons maintenant la même chose pour la décompression dans la routine inf(). inf() décompresse ce qui est, espérons-le, un flux zlib valide à partir du fichier d'entrée et écrit les données non compressées dans le fichier de sortie. Une grande partie de la discussion ci-dessus pour def() s'applique également à inf(), la discussion ici se concentrera donc sur les différences entre les deux.

  1. /* Décompressez du fichier source vers le fichier de destination jusqu'à la fin du flux ou EOF. inf() renvoie 
  2.    Z_OK en cas de succès, Z_MEM_ERROR si la mémoire n'a pas pu être allouée pour le traitement, Z_DATA_ERROR 
  3.    si les données de décompression sont invalides ou incomplètes, Z_VERSION_ERROR si la version de zlib.h et 
  4.    la version de la bibliothèque liée ne correspondent pas, ou Z_ERRNO s'il y a une erreur de lecture ou 
  5.    d'écriture des fichiers.
  6.  */
  7. int inf(FILE *source, FILE *dest)
  8. {

Les variables locales ont les mêmes fonctionnalités que pour def(). La seule différence est qu'il n'y a pas de variable de vidage, car inflate() peut savoir à partir du flux zlib lui-même quand le flux est terminé.

  1. int ret;
  2. unsigned have;
  3. z_stream strm;
  4. unsigned char in[CHUNK];
  5. unsigned char out[CHUNK];

L'initialisation de l'état est la même, sauf qu'il n'y a pas de niveau de compression, bien sûr, et que deux autres éléments de la structure sont initialisés. avail_in et next_in doivent être initialisés avant d'appeler inflateInit(). Cela est dû au fait que l'application a la possibilité de fournir le début du flux zlib afin que inflateInit() ait accès aux informations sur la méthode de compression pour aider à l'allocation de mémoire. Dans l'implémentation actuelle de zlib (jusqu'aux versions 1.2.x), les allocations de mémoire dépendantes de la méthode sont de toute façon différées au premier appel de inflate(). Cependant, ces champs doivent être initialisés car les versions ultérieures de zlib fournissant plus de méthodes de compression peuvent tirer parti de cette interface. Dans tous les cas, aucune décompression n'est effectuée par inflateInit(), donc les champs avail_out et next_out n'ont pas besoin d'être initialisés avant l'appel.

Ici, avail_in est défini sur zéro et next_in est défini sur Z_NULL pour indiquer qu'aucune donnée d'entrée n'est fournie.

  1. /* allouer l'état de inflate */
  2. strm.zalloc = Z_NULL;
  3. strm.zfree = Z_NULL;
  4. strm.opaque = Z_NULL;
  5. strm.avail_in = 0;
  6. strm.next_in = Z_NULL;
  7. ret = inflateInit(&strm);
  8. if (ret != Z_OK) return ret;

La boucle externe do-loop décompresse l'entrée jusqu'à ce que inflate() indique qu'elle a atteint la fin des données compressées et a produit toute la sortie non compressée. Ceci est en contraste avec def() traitant tout le fichier d'entrée. Si la fin du fichier est atteinte avant que les données compressées ne se terminent automatiquement, alors les données compressées sont incomplètes et une erreur est renvoyée.

  1. /* décompresser jusqu'à la fin du flux de dégonflage ou jusqu'à la fin du fichier */
  2. do {

Nous lisons les données d'entrée et définissons la structure strm en conséquence. Si nous avons atteint la fin du fichier d'entrée, nous quittons la boucle externe et signalons une erreur, car les données compressées sont incomplètes. Notez que nous pouvons lire plus de données que ce qui est finalement consommé par inflate(), si le fichier d'entrée continue au-delà du flux zlib. Pour les applications où les flux zlib sont intégrés dans d'autres données, cette routine doit être modifiée pour renvoyer les données inutilisées, ou au moins indiquer la quantité de données d'entrée qui n'a pas été utilisée, afin que l'application sache où récupérer après le flux zlib.

  1. strm.avail_in = fread(in, 1, CHUNK, source);
  2. if (ferror(source)) {
  3.     (void)inflateEnd(&strm);
  4.     return Z_ERRNO;
  5. }
  6. if (strm.avail_in == 0) break;
  7. strm.next_in = in;

La boucle interne do-loop a la même fonction que dans def(), consistant à continuer à appeler inflate() jusqu'à ce qu'elle ait généré toute la sortie possible avec l'entrée fournie.

  1. /* exécuter inflate() sur l'entrée jusqu'à ce que le tampon de sortie ne soit pas plein */
  2. do {

Tout comme dans def(), le même espace de sortie est fourni pour chaque appel de inflate().

  1. strm.avail_out = CHUNK;
  2. strm.next_out = out;

Maintenant, nous exécutons le moteur de décompression lui-même. Il n'est pas nécessaire d'ajuster le paramètre flush, car le format zlib est auto-terminé. La principale différence ici est qu'il existe des valeurs de retour auxquelles nous devons prêter attention. Z_DATA_ERROR indique que inflate() a détecté une erreur dans le format de données compressées zlib, ce qui signifie que soit les données ne sont pas un flux zlib au départ, soit que les données ont été corrompues quelque part en cours de route car elles ont été compressées. L'autre erreur à traiter est Z_MEM_ERROR, pouvant se produire car l'allocation de mémoire est différée jusqu'à ce que inflate() en ait besoin, contrairement à deflate(), dont la mémoire est allouée au début par deflateInit().

Les applications avancées peuvent utiliser deflateSetDictionary() pour amorcer deflate() avec un ensemble de données probables pour améliorer les 32 premiers Ko environ de compression. Cela est noté dans l'entête zlib, donc inflate() demande que ce dictionnaire soit fourni avant de pouvoir commencer la décompression. Sans le dictionnaire, une décompression correcte n'est pas possible. Pour cette routine, nous n'avons aucune idée de ce qu'est le dictionnaire, donc l'indication Z_NEED_DICT est convertie en Z_DATA_ERROR.

inflate() peut également renvoyer Z_STREAM_ERROR, ce qui ne devrait pas être possible ici, mais pourrait être vérifié comme indiqué ci-dessus pour def(). Z_BUF_ERROR n'a pas besoin d'être vérifié ici, pour les mêmes raisons que celles indiquées pour def(). Z_STREAM_END sera vérifié plus tard.

  1. ret = inflate(&strm, Z_NO_FLUSH);
  2. assert(ret != Z_STREAM_ERROR);  /* État non dévasté */
  3. switch (ret) {
  4. case Z_NEED_DICT:
  5.     ret = Z_DATA_ERROR;     /* et tomber à travers */
  6. case Z_DATA_ERROR:
  7. case Z_MEM_ERROR:
  8.     (void)inflateEnd(&strm);
  9.     return ret;
  10. }

La sortie de inflate() est gérée de manière identique à celle de deflate().

  1. have = CHUNK - strm.avail_out;
  2. if (fwrite(out, 1, have, dest) != have || ferror(dest)) {
  3.     (void)inflateEnd(&strm);
  4.     return Z_ERRNO;
  5. }

La boucle interne do-loop se termine lorsque inflate() n'a plus de sortie comme indiqué par le fait que le tampon de sortie ne soit pas rempli, tout comme pour deflate(). Dans ce cas, nous ne pouvons pas affirmer que strm.avail_in sera nul, car le flux deflate peut se terminer avant le fichier.

  1. } while (strm.avail_out == 0);

La boucle externe do-loop se termine lorsque inflate() signale qu'elle a atteint la fin du flux zlib d'entrée, a terminé la décompression et la vérification d'intégrité et a fourni toutes les sorties. Ceci est indiqué par la valeur de retour inflate() Z_STREAM_END. La boucle interne est garantie de laisser ret égal à Z_STREAM_END si le dernier fragment du fichier d'entrée lu contenait la fin du flux zlib. Donc si la valeur de retour n'est pas Z_STREAM_END, la boucle continue à lire plus d'entrées.

  1.     /* terminé lorsque inflate() dit que c'est terminé */
  2. } while (ret != Z_STREAM_END);

À ce stade, la décompression s'est terminée avec succès ou nous sommes sortis de la boucle car il n'y avait plus de données disponibles dans le fichier d'entrée. Si la dernière valeur de retour de inflate() n'est pas Z_STREAM_END, alors le flux zlib était incomplet et une erreur de données est renvoyée. Sinon, nous revenons avec une valeur de retour satisfaisante. Bien sûr, inflateEnd() est appelé en premier pour éviter une fuite de mémoire.

  1.     /* nettoyer et retourner */
  2.     (void)inflateEnd(&strm);
  3.     return ret == Z_STREAM_END ? Z_OK : Z_DATA_ERROR;
  4. }

Cela met fin aux routines qui utilisent directement zlib. Les routines suivantes transforment ce programme en programme de ligne de commande en exécutant les données via les routines ci-dessus de stdin à stdout, et en gérant les erreurs signalées par def() ou inf().

zerr() est utilisé pour interpréter les codes d'erreur possibles de def() et inf(), comme détaillé dans leurs commentaires ci-dessus, et afficher un message d'erreur. Notez qu'il ne s'agit que d'un sous-ensemble des valeurs de retour possibles de deflate() et inflate().

  1. /* Signale une erreur zlib ou d'entrée/sortie */
  2. void zerr(int ret)
  3. {
  4.     fputs("zpipe: ", stderr);
  5.     switch (ret) {
  6.     case Z_ERRNO:
  7.         if (ferror(stdin))
  8.             fputs("Erreur de lecture de stdin\n", stderr);
  9.         if (ferror(stdout))
  10.             fputs("Erreur d'écriture de stdout\n", stderr);
  11.         break;
  12.     case Z_STREAM_ERROR:
  13.         fputs("Niveau de compression invalide\n", stderr);
  14.         break;
  15.     case Z_DATA_ERROR:
  16.         fputs("Données de dégonflage invalides ou incomplètes\n", stderr);
  17.         break;
  18.     case Z_MEM_ERROR:
  19.         fputs("Manque de mémoire\n", stderr);
  20.         break;
  21.     case Z_VERSION_ERROR:
  22.         fputs("Incompatibilité de version zlib !\n", stderr);
  23.     }
  24. }

Voici la routine main() utilisée pour tester def() et inf(). La commande zpipe est simplement un tube de compression de stdin vers stdout, si aucun paramètre n'est fourni, ou c'est un tube de décompression si zpipe -d est utilisé. Si d'autres paramètres sont fournis, aucune compression ou décompression n'est effectuée. À la place, un message d'utilisation est affiché. Les exemples sont zpipe < foo.txt > foo.txt.z pour compresser, et zpipe -d < foo.txt.z > foo.txt pour décompresser.

  1. /* compresser ou décompresser de stdin vers stdout */
  2. int main(int argc, char **argv) {
  3.     int ret;
  4.  
  5.     /* éviter les conversions de fin de ligne */
  6.     SET_BINARY_MODE(stdin);
  7.     SET_BINARY_MODE(stdout);
  8.  
  9.     /* effectuer une compression s'il n'y a pas de paramètres */
  10.     if (argc == 1) {
  11.         ret = def(stdin, stdout, Z_DEFAULT_COMPRESSION);
  12.         if (ret != Z_OK)
  13.             zerr(ret);
  14.         return ret;
  15.     }
  16.  
  17.     /* effectuer la décompression si -d est spécifié */
  18.     else if (argc == 2 && strcmp(argv[1], "-d") == 0) {
  19.         ret = inf(stdin, stdout);
  20.         if (ret != Z_OK)
  21.             zerr(ret);
  22.         return ret;
  23.     }
  24.  
  25.     /* sinon, signaler l'utilisation */
  26.     else {
  27.         fputs("Utilisation de zpipe :zpipe [-d] < source > dest\n", stderr);
  28.         return 1;
  29.     }
  30. }


PARTAGER CETTE PAGE SUR
Dernière mise à jour : Vendredi, le 17 janvier 2025