3. Presentation Language (Langage de présentation)
Ce document traite du formatage des données dans une représentation externe. La syntaxe de présentation très basique et quelque peu définie de manière informelle suivante sera utilisée.
3.1 Basic Block Size (Taille de bloc de base)
La représentation de tous les éléments de données est explicitement spécifiée. La taille du bloc de données de base est d'un octet (c'est-à-dire 8 bits). Les éléments de données de plusieurs octets sont des concaténations d'octets, de gauche à droite, de haut en bas. À partir du flux d'octets, un élément multi-octets (un nombre dans l'exemple suivant) est formé (en utilisant la notation C) comme suit:
value = (byte[0] << 8*(n-1)) | (byte[1] << 8*(n-2)) |
... | byte[n-1];
Cet ordre des octets pour les valeurs multi-octets est l'ordre réseau courant ou format big-endian.
3.2 Miscellaneous (Divers)
Les commentaires commencent par /* et se terminent par */.
Les composants optionnels sont indiqués en les encadrant par [[ ]] (doubles crochets).
Les entités d'un seul octet contenant des données non interprétées sont de type opaque.
Un alias de type T' pour un type existant T est défini par:
T T';
3.3 Numbers (Nombres)
Le type de données numérique de base est un octet non signé (uint8). Tous les types de données numériques plus grands sont formés à partir de séries d'octets de longueur fixe concaténées comme décrit dans la Section 3.1 et sont également non signés. Les types numériques suivants sont prédéfinis:
uint8 uint16[2];
uint8 uint24[3];
uint8 uint32[4];
uint8 uint64[8];
Toutes les valeurs, ici et ailleurs dans la spécification, sont transmises dans l'ordre des octets réseau (big-endian); le uint32 représenté par les octets hexadécimaux 01 02 03 04 est équivalent à la valeur décimale 16909060.
3.4 Vectors (Vecteurs)
Un vecteur (tableau unidimensionnel) est un flux d'éléments de données homogènes. La taille du vecteur peut être spécifiée au moment de la documentation ou laissée non spécifiée jusqu'à l'exécution. Dans les deux cas, la longueur déclare le nombre d'octets, et non le nombre d'éléments, dans le vecteur. La syntaxe pour spécifier un nouveau type, T', qui est un vecteur de longueur fixe de type T est:
T T'[n];
Ici, T' occupe n octets dans le flux de données, où n est un multiple de la taille de T. La longueur du vecteur n'est pas incluse dans le flux encodé.
Dans l'exemple suivant, Datum est défini comme trois octets consécutifs que le protocole n'interprète pas, tandis que Data est trois Datum consécutifs, consommant un total de neuf octets.
opaque Datum[3]; /* trois octets non interprétés */
Datum Data[9]; /* trois vecteurs consécutifs de 3 octets */
Les vecteurs de longueur variable sont définis en spécifiant une sous-plage de longueurs légales, inclusivement, en utilisant la notation <floor..ceiling>. Lorsqu'ils sont encodés, la longueur réelle précède le contenu du vecteur dans le flux d'octets. La longueur sera sous la forme d'un nombre consommant autant d'octets que nécessaire pour contenir la longueur maximale spécifiée du vecteur (ceiling). Un vecteur de longueur variable avec un champ de longueur réelle de zéro est appelé un vecteur vide.
T T'<floor..ceiling>;
Dans l'exemple suivant, mandatory est un vecteur qui doit contenir entre 300 et 400 octets de type opaque. Il ne peut jamais être vide. Le champ de longueur réelle consomme deux octets, un uint16, ce qui est suffisant pour représenter la valeur 400 (voir Section 3.3). De même, longer peut représenter jusqu'à 800 octets de données, ou 400 éléments uint16, et il peut être vide. Son encodage inclura un champ de longueur réelle de deux octets ajouté au début du vecteur. La longueur d'un vecteur encodé doit être un multiple pair de la longueur d'un seul élément (par exemple, un vecteur de 17 octets de uint16 serait illégal).
opaque mandatory<300..400>;
/* le champ de longueur fait deux octets, ne peut pas être vide */
uint16 longer<0..800>;
/* zéro à 400 entiers non signés de 16 bits */
3.5 Enumerateds (Énumérations)
Un type de données clairsemé supplémentaire, appelé enum, est disponible. Chaque définition est un type différent. Seuls les énumérés du même type peuvent être assignés ou comparés. Chaque élément d'un énuméré doit se voir attribuer une valeur, comme démontré dans l'exemple suivant. Puisque les éléments de l'énuméré ne sont pas ordonnés, ils peuvent se voir attribuer n'importe quelle valeur unique, dans n'importe quel ordre.
enum { e1(v1), e2(v2), ... , en(vn) [[, (n)]] } Te;
Les extensions futures ou ajouts au protocole peuvent définir de nouvelles valeurs. Les implémentations doivent être capables d'analyser et d'ignorer les valeurs inconnues, sauf si la définition du champ indique le contraire.
Un énuméré occupe autant d'espace dans le flux d'octets que sa valeur ordinale maximale définie. La définition suivante entraînerait l'utilisation d'un octet pour transporter les champs de type Color:
enum { red(3), blue(5), white(7) } Color;
On peut optionnellement spécifier une valeur sans son tag associé pour forcer la définition de largeur sans définir un élément superflu.
Dans l'exemple suivant, Taste consommera deux octets dans le flux de données mais ne peut assumer que les valeurs 1, 2 ou 4 dans la version actuelle du protocole.
enum { sweet(1), sour(2), bitter(4), (32000) } Taste;
Les noms des éléments d'une énumération ont une portée limitée au type défini. Dans le premier exemple, une référence pleinement qualifiée au deuxième élément de l'énumération serait Color.blue. Une telle qualification n'est pas requise si la cible de l'assignation est bien spécifiée.
Color color = Color.blue; /* surspécifié, légal */
Color color = blue; /* correct, type implicite */
Les noms des éléments ne sont pas tenus d'être uniques. La valeur numérique parmi les éléments doit être unique, cependant.
enum { low(1), medium(2), high(2) } Priority; /* INCORRECT */
Pour les énumérés qui ne sont jamais convertis en représentation externe, l'information numérique peut être omise.
enum { low, medium, high } Priority;
3.6 Constructed Types (Types construits)
Les types de structure peuvent être construits à partir de types primitifs pour plus de commodité. Chaque spécification déclare un nouveau type unique. La syntaxe de définition ressemble beaucoup à celle de C:
struct {
T1 f1;
T2 f2;
...
Tn fn;
} T;
Les champs vectoriels de longueur fixe et variable sont autorisés en utilisant la syntaxe vectorielle standard. Les structures V1 et V2 dans l'exemple de variantes (Section 3.8) démontrent cela.
Les champs au sein d'une structure peuvent être qualifiés en utilisant le nom du type, avec une syntaxe très similaire à celle disponible pour les énumérés. Par exemple, T.f2 fait référence au deuxième champ de la déclaration précédente.
3.7 Constants (Constantes)
Les champs et variables peuvent se voir attribuer une valeur fixe en utilisant =, comme dans:
struct {
T1 f1 = 8; /* T.f1 doit toujours être 8 */
T2 f2;
} T;
3.8 Variants (Variantes)
Les structures définies peuvent avoir des variantes basées sur certaines connaissances disponibles dans l'environnement. Le sélecteur doit être un type énuméré qui définit les variantes possibles que la structure définit. Chaque branche de la structure variante spécifie le type du champ de cette variante et une étiquette de champ optionnelle. Le mécanisme par lequel la variante est sélectionnée à l'exécution n'est pas prescrit par le langage de présentation.
struct {
T1 f1;
T2 f2;
....
Tn fn;
select (E) {
case e1: Te1 [[fe1]];
case e2: Te2 [[fe2]];
....
case en: Ten [[fen]];
};
} Tv;
Par exemple:
enum { apple, orange, banana } VariantTag;
struct {
uint16 number;
opaque string<0..10>; /* longueur variable */
} V1;
struct {
uint32 number;
opaque string[10]; /* longueur fixe */
} V2;
struct {
VariantTag type;
select (VariantRecord.type) {
case apple: V1;
case orange:
case banana: V2;
};
} VariantRecord;