Image logo.gif


LA LIBRAIRIE SDIF

Documentation du code C

Analyse/Synthèse

Dominique Virolle



Types de bas niveau, Valeurs et Variables Globales

Formalisme des noms C dans la librairie SDIF

Sauf exceptions, les noms des types, structures, noeuds, listes... suivent ce formalisme:

Mot, Lettre Définition
S structure (déclarée par struct).
T type (déclarée par typedef).
N noeud (Node), lié à une structure contenant un champ "Next".
L liste (List), contient un champ "Head" qui est un Node.
E énumération (déclarée par enum).
HT table de hashage
_Sdif précompilé.
e valeur énuméré par un enum.
g variable global.
M_ types de matrice prédéfini.
F_ types de frame prédéfini.
Sdiff il y a un argument de type FILE *f.
SdifF SdifFileT* qui est la structure de plus haut niveau dans la librairie.
U unsigned.
R et W Read et Write.
Size Nombre de bytes (size_t)
Hash type bas niveau HashTableT*.
Error détection d'erreur grave (débugage essentiellement).
MatrixType type de matrice (prédéfini ou utilisateur).
FrameType type de frame (prédéfini ou utilisateur).
Matrix bloc de données matrice.
Frame bloc de données frame.
Test test d'erreur haut niveau sur un fichier SDIF (SdifFileT).
File fonctionnalité de haut niveau sur des instances de SdifFileT.
Get et Put lecture et écriture communes aux fichiers SDIF binaires et aux fichiers pseudo-SDIF textes. Ce sont aussi les lectures et les écritures de certaines structures.
Read et Write lecture et écriture exclusivement sur fichiers SDIF binaires.
Print écriture texte. SdifFPrint pour un fichier pseudo-SDIF et SdifPrint pour des sorties sur stdout ou stderr (débugage).
Scan lecture exclusivement sur fichiers pseudo-SDIF textes.
TextConv conversion de texte à binaire.
ConvToText conversion de binaire à texte.
All tous les éléments d'un certain type dans 1 niveau de structure de fichier SDIF (ex AllMatrix : toutes les matrices d'1 frame).
One 1 seul élément parmi une succession d'éléments de même sorte.
TimePosition position disque par rapport au temps.
Create construction d'un pointeur sur élément.
Kill destruction d'un pointeur. Dans le cas d'un noeud, la fonction retourne le Next
Num numéro (identification).
Nb nombre de... (cardinal).
Head et Tail 1er et dernier élément d'une liste.
Curr courant. Cela peut-être un noeud, une signature, une entête de bloc...
iAAA ième AAA (compteur)

Types de bas Niveau

Dépendances matériel, système, compilateur

Taille d'une adresse (sizeof(void*))

La seule dépendance liée au matériel ou au système indiquée par un #ifdef est la taille d'une adresse. La librairie fonctionne sur les OS 32 et 64 bits (16 bits non prévu). On a ainsi:

/* dans SdifRWLowLevel.h*/
#if defined(__mips64) || defined(__alpha)
#define _LONG64BITS_
#else
#define _LONG32BITS_
#endif

Position dans un fichier

Il y a une dépendance sur le compilateur au niveau du type C fpos_t. Certains compilateurs ont fpos_t défini comme un long. Pour d'autres, fpos_t est une structure; on ne peut donc pas faire simplement des opérations arithmétiques directement sur les variables (ex : Macintosh MacOS8, CodeWarrior 10).
Dans SdifGlobals.h, on a un faux (précompilé) type SdifFPosT qui permet d'avoir la position d'un fichier codé sur un long. La taille de de ce type dépend donc du système : sur IRIX 5.3, Macintosh... c'est 4 bytes, et sur IRIX64 et Alpha on a 8 bytes. Les fonctions de positionnement sont donc toujours associés au système automatiquement (pas de réduction de 8 bytes à 4 bytes sur les machines 64 bits).

/* SdifGlobals.h  fpos_t compatible sur Macintosh */
#ifdef MACINTOSH
#define SdifFPosT long
#define SdifFGetPos(f,p)    ((((*(p)) = ftell(f)) == -1) ? -1 : 0)
#define SdifFSetPos(f,p)    fseek(f, (*(p)), SEEK_SET)
#else
#define SdifFPosT fpos_t
#define SdifFGetPos(f,p)    fgetpos((f),(p))
#define SdifFSetPos(f,p)    fsetpos((f),(p))
#endif

Nombres

SdifGlobals.h : Sont définis des types de très bas niveau représentant les int, float (unsigned) sur 2, 4, 8 bytes. Cela sous-entend que le matèriel satisfasse :

La librairie SDIF ne fait pas la vérification de ces égalités mais elles sont en général vraies pour des OS 32 et 64 bits.

On a ainsi les types:

  
typedef short          SdifInt2;
typedef unsigned short SdifUInt2;
typedef int            SdifInt4;
typedef unsigned int   SdifUInt4;
typedef float          SdifFloat4;
typedef double         SdifFloat8;

Est associé aux différents types de bas niveau, un type d'énumération qui permet de représenter les différents types avec un codage sur 4 bytes.

typedef enum SdifDataTypeE
{
  eFloat4 = 0x20,
  eFloat8 = 0x40,
  eInt2   = 0x1010,
  eUInt2  = 0x1110,
  eInt4   = 0x1020,
  eUInt4  = 0x1120,
  eChar4  = 0x2020
} SdifDataTypeET;

Signature

Toujours dans SdifGlobals.h, est défini le type SdifSignature. Une signature étant un mot de 4 bytes, les signatures sont représentées par un unsigned int. Est associée à ce type une énumération des signatures de base, qui sont celles des chunks spéciaux. Les types de frames et de matrices prédéfinis n'y sont pas car tous les types de données sont interprétés et évolutifs.

typedef unsigned int   SdifSignature;
typedef enum SdifSignatureE
{
  eSDIF = 'SDIF' ,         /* SDIF header */
  e1NVT = '1NVT' ,         /* Name Value Table */
  e1TYP = '1TYP' ,         /* TYPe declarations */
  e1MTD = '1MTD' ,         /* Matrix Type Declaration */
  e1FTD = '1FTD' ,         /* Frame Type Declaration */
  e1IDS = '1IDS' ,         /* ID Stream Table */
  eSDFC = 'SDFC' ,         /* Start Data Frame Chunk (text files) */
  eENDC = 'ENDC' ,         /* END Chunk (text files) */
  eENDF = 'ENDF' ,         /* END File (text files) */
  eFORM = 'FORM' ,         /* FORM for IFF compatibility (obsolete ?) */
  eEmptySignature = '\0\0\0\0'
} SdifSignatureET;

Quelques fonctions sont directements liées aux signatures:

#define _SdifNbMaxPrintSignature 8
char  gSdifStringSignature[_SdifNbMaxPrintSignature][5];

char* SdifSignatureToString(SdifSignature Signature);
short SdifSignatureCmpNoVersion(SdifSignature Signature1, SdifSignature Signature2);

SdifSignatureToString permet de renvoyer un pointeur sur une chaine de caractères contenant les caractères de la signature passée en argument avec un caractère de fin de chaine. Ce pointeur est directement utilisable avec les fonctions de sorties standarts formatées. _SdifNbMaxPrintSignature (8) et gSdifStringSignature servent uniquement à cette fonction. gSdifStringSignature est un tableau de 8 chaines de caractères, chacune de taille 5. SdifSignatureToString utilise ce tableau de façon cyclique pour son résultat. On évite ainsi de s'occuper de la mémoire pour ces convertions de signature en chaine de caractères. Toutefois, on ne peut pas utiliser SdifSignatureToString plus de 8 fois dans les paramètres d'une fonctions. On ne pourra pas afficher 9 signatures avec la même instruction printf. La première affichié serait la 9ième car c'est la même position mémoire. Actuellement, aucune utilisation de SdifSignatureToString n'a dépassée 2 signatures sur une même instruction..

SdifSignatureCmpNoVersion permet de comparer 2 signatures sans le premier byte. Ainsi on peut tenter des lectures sur des matrices ou des frames de version inconnu par le programme lecteur (la librairie doit tout de même connaître les signatures).

type table de hashage

SdifHash.h
SdifHash.c

SdifHashIndexUnion est un type qui permet de définir le type d'index d'une table de hashage. On peut en effet avoir des tables indexée par des chaînes de caratères (méthode classique) et d'autres directement par des entiers. Le type est défini par un union qui est soit un unsigned int sur 4 bytes soit un tableau de 1 pointeur sur un chaine de caractère. La représentation sous forme de tableau sert uniquement à fixer la taille d'une variable de SdifHashIndexUnion à 4 bytes (ou 8 si la machine est 64 bits comme Dec Alpha).

  
typedef union SdifHashIndexU
{
  char* Char[1]; /* tab of one pointer to fixe union size at 4 or 8 bytes */
  unsigned int  Int4;
} SdifHashIndexUT;

SdifHashIndexTypeEnum est un enum qui permet de dire quel type de l'union est utilisé dans une table de hashage.

typedef enum SdifHashIndexTypeE
{
  eHashChar,
  eHashInt4
} SdifHashIndexTypeET;

Le type table de hashage est défini comme suit:

typedef struct SdifHashNS SdifHashNT;

struct SdifHashNS 
{
  SdifHashNT *Next;
  SdifHashIndexUT Index;
  void* Data;
};

typedef struct SdifHashTableS
{
  SdifHashNT* *Table;
  unsigned int HashSize;
  SdifHashIndexTypeET IndexType;
  void (*Killer)();  /* no verification of arguments */
  unsigned int NbOfData;
} SdifHashTableT;

Le champ Table est un pointeur sur un tableau de noeuds de donnée hashée SdifHashNT. Le champ Data du noeud n'est pas typé. C'est à dire que l'on peut créer des tables de hashages contenant des objets de type quelconque. Mais pour une table de hashage donnée, tous les objets sont de même type et de même allocation (statique ou dynamique).
Le seul champ du type SdifHashTableT contenant une information sur le type de données de la table est Killer qui est un pointeur sur la fonction de destruction associée. Dans le cas d'une table à données statiques, Killer doit être NULL. Les tables de hashages sont souvent utilisées dans la librairie. Les fichiers SdifHash.* n'utilisent aucunes informations des autres fichiers.

La taille d'une table de hashage doit être un nombre premier. C'est le cas de beaucoup de nombres (2^n - 1).

Constantes Précompilées

Token Valeur Fichier Définition
_SdifUnknownSize 0xffffffff SdifGlobals.h Valeur donnée à un champ indiquant une taille de bloc inconnue.
_SdifPadding 8 SdifGlobals.h Nombre de bytes d'alignement : 8 bytes => alignement sur 64 bits.
_SdifFloat8Error 0xffffffff SdifGlobals.h Retour erronée de lecture d'un float (à modifier).
_SdifStringLen 1024 SdifGlobals.h Taille d'une chaine de caractère à memoire statique
_SdifTypesFileName "SdifTypes.STYP"SdifGlobals.h Nom du fichier contenant la base de données des types prédéfinis
_SdifBSLittleE 4096 SdifRWLowLevel.hTaille du buffer maximum pour les lectures, écritures binaires sur fichier.
_SdifPaddingChar '\0' SdifRWLowLevel.hCaractère utilisé pour le Padding
_SdifReservedChars ",;{}[]:" SdifRWLowLevel.hChaine de caractères contenant les caractères résevés de Sdif
_SdifFrameHeaderSize 24 SdifFrame.h Taille de l'entête d'un frame (constant pour le format).
_SdifGenHashSize 127 SdifGlobals.h Taille des tables de hashage globales (doit être un nombre premier)
_SdifNameValueHashSize31 SdifNameValue.h Taille des tables de hashage d'informations
_SdifGranule 1024 SdifGlobals.h Taille des blocs d'allocation pour le type SdifOneRowT
_SdifFloatEps 1.0e-20 SdifGlobals.h Valeur epsilon floattante

Variables Globales

Type de Machine gSdifMachineType

Le type de la machine (BigEndian ou LittleEndian) est détermineé par la librairie SDIF à l'exécution d'un programme et non en précompilation des sources SDIF. Ainsi, on a une variable globale gSdifMachineType accessible partout. Sa valeur est d'un type énuméré SdifMachineEnum qui propose plusieurs type de machines (SdifRWLowLevel.h). Une fois que cette variable est affectée, il n'est plus nécessaire de la modifier. La fonction assiociée à la détermination du type de machine est SdifMachineEnum SdifGetMachineType(void).

typedef enum SdifMachineE
{
  eUndefinedMachine,
  eBigEndian,
  eLittleEndian,
  eBigEndian64,
  eLittleEndian64,
  ePDPEndian
} SdifMachineET;

extern SdifMachineET gSdifMachineType;

extern SdifMachineET SdifGetMachineType(void);
extern SdifMachineET SdifInitMachineType(void);

Chaines de caractères de transfert

Il est souvent nécessaire de lire ou d'écrire des chaines de caractères dans un fichier et de les recopier dans des pointeurs de char alloués à la taille ajustée.

extern char gSdifString[_SdifStringLen];
extern char gSdifString2[_SdifStringLen];
extern char gSdifErrorMess[_SdifStringLen];

gSdifString et gSdifString2 sont utilisées pour les lectures essentiellement. Elle sont déclarées en taille statique (_SdifStringLen == 1024).
gSdifErrorMess est déclarée comme les précédentes mais est utilisée exclusivement pour les constructions de messages d'erreur par des sprintf.

Erreurs

SdifError.h
SdifError.c

 typedef enum SdifErrorE
{
  eFalse = 0,
  eTrue = 1,
  eFreeNull = 256,
  eAllocFail,
  eArrayPosition,
  eEof,
  eFileNotFound,
  eInvalidPreType,
  eAffectationOrder,
  eNoModifErr,
  eNotInDataTypeUnion,
  eNotFound,
  eExistYet,
  eWordCut,
  eTokenLength
} SdifErrorEnum;

Il s'agit des erreurs dont la detection implique souvent une erreur de programmation (Sauf pour les erreurs sur fichiers). Elles provoquent souvent une sortie violante exit(1). eFalse et eTrue ne sont qu'une autre représentation de 0 ou 1. Les erreurs d'interprétation des fichiers dûes par exemple à un type non défini sont gérées par les tests de haut niveau (SdifTest.*).


Structure du type SdifFileT

La librairie permet de contrôler plusieurs fichiers SDIF en lecture ou écriture simultanément. Ils sont représentés par une structure de haut niveau SdifFileS défini dans SdifFileStruct.h. Ils ont en commun la base des types prédéfinis (qui est elle-même considérée comme une structure SdifFileT). Ils ont chacun :

Le principe de la structure SdifFileS est de considérer: La bdd permet de récupérer des paramètres ou d'en ajouter à tout moment, independamment du fichier sur disque. Les deux flots, un binaire, un texte permet de conserver une identité forte sur le fichier binaire et de réaliser des conversions de façon simple.

NameValues

SdifNameValuesLT* NameValues
Liste de tables de hashage de type SdifHashTableT*, où chaque table correspond à un chunk d'informations.

L'indexation des tables est par le nom de l'information et les données sont du type SdifNameValueT* dont le destructeur est SdifKillNameValue(*). La taille de chaque petite table d'information est _SdifNameValueHashSize.

MatrixTypesTable

SdifHashTableT* MatrixTypesTable
Table contenant tout les types de matrices associées à un fichier.

L'indexation de la table est par un entier représentant le hashage de la signature. Les données de cette table sont de type SdifMatrixTypeT* dont le destructeur est SdifKillMatrixType(*) (SdifMatrixType.*).

FrameTypesTable

SdifHashTableT* FrameTypesTable
Table contenant tout les types de frames associés à un fichier.

L'indexation de la table est par un entier représentant le hashage de la signature. Les données de cette table sont de type SdifFrameTypeT* dont le destructeur est SdifKillFrameType(*) (SdifFrameType.*).

StreamIDsTable

SdifHashTableT* StreamIDsTable
Table contenant tous les Stream IDs associés à un fichier.

L'indexation est par l'entier numID des Stream IDs. La taille de la table de hashage est 1, donc il y a continuellement colision, il s'agit ainsi d'une liste triée par les IDs. Ceci uniquement pour que les fichiers SDIF est ses Stream IDs triés. Aussi, pour des renumérotations des IDs, on pourrait préférer cette structure triée.

Les données de la table sont de type SdifStreamIDT* dont le destructeur est SdifKillStreamID(*) (SdifStreamID.*).

Types Prédéfinis

Variable globale: SdifFileT *gSdifPredefinedTypes
Les types prédéfinis de SDIF sont interprétés par la librairie au même titre que les types spécifiques à un fichier. Ceci pour deux raisons:

Ainsi gSdifPredefinedTypes est une instance (pointeur) de SdifFileT dont le mode d'ouverture est la lecture texte seulement. Stream est donc fermé et TextStream est ouvert en lecture le temps du chargement. Si le fichier texte contenant les types prédéfinis est introuvable, alors la librairie charge les types précodés statiquement dans SdifPreType.*. Dans ce cas un message sur stderr indique que la base des types prédéfinis peut être incomplète.

Lien vers les types prédéfinis

La liaison entre gSdifPredefinedTypes et les autres fichiers se fait à partir des types (C) SdifMatrixTypeT et SdifFrameTypeT dont les structures contiennent un lien ("Pre") sur les types prédéfinis. Dans gSdifPredefinedTypes les type sont tous dans les listes "Use" et les liste "Pre" sont vides. La mise à jour des liens ce fait ainsi:

/* SdifF est un pointeur sur SdifFileT.
 * Signature est la signature du type de matrice.
 * SdifGetMatrixType(SdifF->MatrixTypesTable, Signature) ne recherche le type
 * que dans la table des types de matrices de SdifF et non dans celle
 * de gSdifPredefinedTypes.
 * Si le type n'existe pas déjà, alors on le recherche dans
 * gSdifPredefinedTypes->MatrixTypesTable. Si on le trouve, alors
 * on crée un type de matrice en donnant le type predefini en 2ième
 * argument de SdifCreateMatrixType puis on ajoute le type à la table de SdifF.
 */

if (! SdifGetMatrixType(SdifF->MatrixTypesTable, Signature))
  {
    if (PredefinedMatrixType = SdifGetMatrixType(gSdifPredefinedTypes->MatrixTypesTable, Signature))
      {
        SdifCreateMatrixType(Signature, PredefinedMatrixType);
        SdifPutMatrixType(SdifF->MatrixTypesTable, MatrixType);
      }
    else
      ; /* le type n'existe pas */
  }
else
  ; /* le type existe déjà */

/* idem pour les types de frame avec SdifGetFrameType et SdifPutFrameType.
 */


Structures des types interprétés

MatrixTypeT

typedef struct SdifColumnDefS
{
  char *Name;
  SdifUInt4 Num;
} SdifColumnDefT;

typedef struct SdifColumnDefNS SdifColumnDefNT;

struct SdifColumnDefNS
{
  SdifColumnDefNT *Next;
  SdifColumnDefT *ColumnDef;
};

typedef struct SdifMatrixTypeS SdifMatrixTypeT;

struct SdifMatrixTypeS
{
  SdifSignature      Signature;

  SdifMatrixTypeT    *MatrixTypePre;

  SdifColumnDefNT *HeadUse;
  SdifColumnDefNT *TailUse;
  SdifUInt4       NbColumnDefUse;

  SdifUInt4       NbColumnDef;
  SdifModifModeET ModifMode;
};

Un type de matrice est défini comme une signature associée à deux listes dont la concaténation est la suite des colonnes d'un bloc matrice. La première est la liste des colonnes du type prédéfini s'il existe. La seconde est la liste des colonnes de complétion.

Ainsi MatrixTypePre est le lien sur le type prédéfini qu'il faut mettre à jour à la rencontre de la signature dans les fichiers Stream ou Stream ou lors d'ajout "manuel" de colonnes en complétion. Ce lien est NULL s'il n'y a pas de type prédéfini pour la signature considérée.(cf. Lien vers les types prédéfinis)

HeadUse et TailUse sont des acces sur les colonnes de complétion. Ainsi on retrouve toutes les colonnes d'un type de matrice en parcourant MatrixTypePre->HeadUse jusqu'à MatrixTypePre->TailUse puis HeadUse jusqu'à TailUse. NbColumnDef est le nombre total de colonnes du type de matrice.

La contruction des listes de colonnes se fait par insertion à la queue pour respecter l'ordonnancement des colonnes imposé par la norme.

FrameTypeT

typedef struct SdifComponentS
{
  SdifSignature MatrixSignature;
  char *Name;
  SdifUInt4  Num;
} SdifComponentT;



typedef struct SdifComponentNS SdifComponentNT;
struct SdifComponentNS
{
  SdifComponentNT *Next;
  SdifComponentT *Component;
};



typedef struct SdifFrameTypeS SdifFrameTypeT;
struct SdifFrameTypeS
{
  SdifSignature Signature;

  SdifFrameTypeT* FrameTypePre;

  SdifComponentNT *HeadUse;
  SdifComponentNT *TailUse;
  SdifUInt4       NbComponentUse;

  SdifUInt4       NbComponent;
  SdifModifModeET ModifMode;
};

Les types de frames fonctionnent comme les types de matrices. Un type de frames étant une signature associée à des composants réparties en deux listes, celle du type de frame prédéfini FrameTypePre->HeadUse et celle du type de frame local HeadUse.

Les composants sont une signature de type de matrice, et un nom. Il est donc nécessaire de mettre à jour les types de matrice, c'est à dire créer le lien vers le type prédéfini s'il existe, lorsque l'on ajoute un nouveau composant au type de frame.(cf. Lien vers les types prédéfinis)

pseudo-SDIF TextStream

Pour pouvoir créer manuellement et lire des fichiers SDIF, la librairie possède une gestion d'un format pseudo-SDIF texte. Toutes les données d'un fichier SDIF sont convertibles en texte exceptés les tailles de chunks. Les chunks ASCII ont un contenu identique pour les fichiers SDIF binaires et les fichiers texte