Zum Hauptinhalt springen

3. Presentation Language (Darstellungssprache)

Dieses Dokument befasst sich mit der Formatierung von Daten in einer externen Darstellung. Die folgende sehr grundlegende und etwas informell definierte Präsentationssyntax wird verwendet.

3.1 Basic Block Size (Grundlegende Blockgröße)

Die Darstellung aller Datenelemente ist explizit spezifiziert. Die grundlegende Datenblockgröße beträgt ein Byte (d.h. 8 Bits). Mehrbyte-Datenelemente sind Verkettungen von Bytes, von links nach rechts, von oben nach unten. Aus dem Byte-Stream wird ein Mehrbyte-Element (eine Zahl im folgenden Beispiel) (unter Verwendung der C-Notation) wie folgt gebildet:

value = (byte[0] << 8*(n-1)) | (byte[1] << 8*(n-2)) |
... | byte[n-1];

Diese Byte-Reihenfolge für Mehrbyte-Werte ist die übliche Netzwerk-Byte-Reihenfolge oder Big-Endian-Format.

3.2 Miscellaneous (Verschiedenes)

Kommentare beginnen mit /* und enden mit */.

Optionale Komponenten werden durch Einschließen in [[ ]] (doppelte Klammern) gekennzeichnet.

Einzelbyte-Entitäten, die nicht interpretierte Daten enthalten, sind vom Typ opaque.

Ein Typalias T' für einen bestehenden Typ T wird definiert durch:

T T';

3.3 Numbers (Zahlen)

Der grundlegende numerische Datentyp ist ein vorzeichenloses Byte (uint8). Alle größeren numerischen Datentypen werden aus festen Byte-Serien gebildet, die wie in Abschnitt 3.1 beschrieben verkettet werden, und sind ebenfalls vorzeichenlos. Die folgenden numerischen Typen sind vordefiniert:

uint8 uint16[2];
uint8 uint24[3];
uint8 uint32[4];
uint8 uint64[8];

Alle Werte, hier und anderswo in der Spezifikation, werden in Netzwerk-Byte-Reihenfolge (Big-Endian) übertragen; das durch die Hexadezimal-Bytes 01 02 03 04 dargestellte uint32 entspricht dem Dezimalwert 16909060.

3.4 Vectors (Vektoren)

Ein Vektor (eindimensionales Array) ist ein Strom homogener Datenelemente. Die Größe des Vektors kann zur Dokumentationszeit spezifiziert oder bis zur Laufzeit unspezifiziert gelassen werden. In beiden Fällen deklariert die Länge die Anzahl der Bytes, nicht die Anzahl der Elemente im Vektor. Die Syntax zur Spezifizierung eines neuen Typs, T', der ein Vektor fester Länge vom Typ T ist, lautet:

T T'[n];

Hier belegt T' n Bytes im Datenstrom, wobei n ein Vielfaches der Größe von T ist. Die Länge des Vektors ist nicht im codierten Stream enthalten.

Im folgenden Beispiel ist Datum als drei aufeinanderfolgende Bytes definiert, die das Protokoll nicht interpretiert, während Data drei aufeinanderfolgende Datum sind, die insgesamt neun Bytes verbrauchen.

opaque Datum[3];      /* drei nicht interpretierte Bytes */
Datum Data[9]; /* drei aufeinanderfolgende 3-Byte-Vektoren */

Vektoren variabler Länge werden definiert, indem ein Teilbereich zulässiger Längen, inklusiv, unter Verwendung der Notation <floor..ceiling> spezifiziert wird. Wenn diese codiert werden, steht die tatsächliche Länge vor dem Inhalt des Vektors im Byte-Stream. Die Länge hat die Form einer Zahl, die so viele Bytes verbraucht, wie erforderlich sind, um die spezifizierte Maximallänge (ceiling) des Vektors zu halten. Ein Vektor variabler Länge mit einem tatsächlichen Längenfeld von Null wird als leerer Vektor bezeichnet.

T T'<floor..ceiling>;

Im folgenden Beispiel ist mandatory ein Vektor, der zwischen 300 und 400 Bytes vom Typ opaque enthalten muss. Er kann niemals leer sein. Das tatsächliche Längenfeld verbraucht zwei Bytes, ein uint16, was ausreicht, um den Wert 400 darzustellen (siehe Abschnitt 3.3). Ähnlich kann longer bis zu 800 Bytes an Daten oder 400 uint16-Elemente darstellen, und er kann leer sein. Seine Codierung wird ein zweistelliges tatsächliches Längenfeld enthalten, das dem Vektor vorangestellt wird. Die Länge eines codierten Vektors muss ein gerades Vielfaches der Länge eines einzelnen Elements sein (zum Beispiel wäre ein 17-Byte-Vektor von uint16 illegal).

opaque mandatory<300..400>;
/* Längenfeld ist zwei Bytes, kann nicht leer sein */
uint16 longer<0..800>;
/* null bis 400 16-Bit-vorzeichenlose Ganzzahlen */

3.5 Enumerateds (Aufzählungen)

Ein zusätzlicher spärlicher Datentyp, genannt enum, ist verfügbar. Jede Definition ist ein anderer Typ. Nur Aufzählungen desselben Typs können zugewiesen oder verglichen werden. Jedem Element einer Aufzählung muss ein Wert zugewiesen werden, wie im folgenden Beispiel demonstriert. Da die Elemente der Aufzählung nicht geordnet sind, können ihnen beliebige eindeutige Werte in beliebiger Reihenfolge zugewiesen werden.

enum { e1(v1), e2(v2), ... , en(vn) [[, (n)]] } Te;

Zukünftige Erweiterungen oder Ergänzungen des Protokolls können neue Werte definieren. Implementierungen müssen in der Lage sein, unbekannte Werte zu parsen und zu ignorieren, es sei denn, die Definition des Feldes besagt etwas anderes.

Eine Aufzählung belegt so viel Platz im Byte-Stream wie ihr maximal definierter Ordinalwert. Die folgende Definition würde dazu führen, dass ein Byte verwendet wird, um Felder vom Typ Color zu tragen:

enum { red(3), blue(5), white(7) } Color;

Man kann optional einen Wert ohne sein zugehöriges Tag spezifizieren, um die Breitendefinition zu erzwingen, ohne ein überflüssiges Element zu definieren.

Im folgenden Beispiel wird Taste zwei Bytes im Datenstrom verbrauchen, kann aber in der aktuellen Version des Protokolls nur die Werte 1, 2 oder 4 annehmen.

enum { sweet(1), sour(2), bitter(4), (32000) } Taste;

Die Namen der Elemente einer Aufzählung sind innerhalb des definierten Typs begrenzt. Im ersten Beispiel wäre ein vollständig qualifizierter Verweis auf das zweite Element der Aufzählung Color.blue. Eine solche Qualifizierung ist nicht erforderlich, wenn das Ziel der Zuweisung gut spezifiziert ist.

Color color = Color.blue;     /* überspezifiziert, legal */
Color color = blue; /* korrekt, Typ implizit */

Die Namen der Elemente müssen nicht eindeutig sein. Der numerische Wert unter den Elementen muss jedoch eindeutig sein.

enum { low(1), medium(2), high(2) } Priority;  /* FALSCH */

Für Aufzählungen, die niemals in externe Darstellung konvertiert werden, kann die numerische Information weggelassen werden.

enum { low, medium, high } Priority;

3.6 Constructed Types (Konstruierte Typen)

Strukturtypen können aus primitiven Typen zur Bequemlichkeit konstruiert werden. Jede Spezifikation deklariert einen neuen, eindeutigen Typ. Die Syntax für die Definition ähnelt sehr der von C:

struct {
T1 f1;
T2 f2;
...
Tn fn;
} T;

Vektorfelder fester und variabler Länge sind unter Verwendung der Standard-Vektorsyntax erlaubt. Die Strukturen V1 und V2 im Varianten-Beispiel (Abschnitt 3.8) demonstrieren dies.

Die Felder innerhalb einer Struktur können unter Verwendung des Typnamens qualifiziert werden, mit einer Syntax, die der für Aufzählungen verfügbaren sehr ähnlich ist. Zum Beispiel bezieht sich T.f2 auf das zweite Feld der vorherigen Deklaration.

3.7 Constants (Konstanten)

Feldern und Variablen kann ein fester Wert unter Verwendung von = zugewiesen werden, wie in:

struct {
T1 f1 = 8; /* T.f1 muss immer 8 sein */
T2 f2;
} T;

3.8 Variants (Varianten)

Definierte Strukturen können Varianten basierend auf einigem in der Umgebung verfügbaren Wissen haben. Der Selektor muss ein Aufzählungstyp sein, der die möglichen Varianten definiert, die die Struktur definiert. Jeder Arm der Variantenstruktur spezifiziert den Typ des Feldes dieser Variante und ein optionales Feldlabel. Der Mechanismus, durch den die Variante zur Laufzeit ausgewählt wird, ist von der Präsentationssprache nicht vorgeschrieben.

struct {
T1 f1;
T2 f2;
....
Tn fn;
select (E) {
case e1: Te1 [[fe1]];
case e2: Te2 [[fe2]];
....
case en: Ten [[fen]];
};
} Tv;

Zum Beispiel:

enum { apple, orange, banana } VariantTag;

struct {
uint16 number;
opaque string<0..10>; /* variable Länge */
} V1;

struct {
uint32 number;
opaque string[10]; /* feste Länge */
} V2;

struct {
VariantTag type;
select (VariantRecord.type) {
case apple: V1;
case orange:
case banana: V2;
};
} VariantRecord;