Bitmanipulation

Ein sehr gutes Beispiel für die Erzeugung von Code zur Kompilierzeit in D mit Mixins ist Bitmanipulation.

Einfache Bitmanipulation

D bietet die folgenden Operatoren für Bitmanipulation:

  • & bitweises Und
  • | bitweises Oder
  • ~ bitweise Negation
  • << bitweise vorzeichenbewahrende Links-Verschiebung
  • >> bitweise vorzeichenbewahrende Rechts-Verschiebung
  • >>> bitweise vorzeichenlose Rechts-Verschiebung

Ein praktisches Beispiel

Ein gängiges Beispiel für Bitmanipulation ist das Einlesen eines Bitwertes. D bietet core.bitop.bt für die meisten allgemeinen Aufgaben. Nichtsdestotrotz werden wir zum besseren Verständnis die ausführliche Implementierung eines Bit-Tests vornehmen:

enum posA = 1;
enum maskA = (1 << posA);
bool getFieldA()
{
    return _data & maskA;
}

Eine Verallgemeinerung ist der Test von Bitblöcken, die aus mehreren aneinandergereihten Bits bestehen. Dazu wird eine spezielle Maske der Länge des Blocks benötigt. Der Datenblock wird entsprechend verschoben, bevor die Maske angewendet wird:

enum posA = 1;
enum lenA = 3;
enum maskA = (1 << lenA) - 1; // ...0111
uint getFieldA()
{
    return (_data >> posA) & maskA;
}

Das Setzen solch eines Blocks kann genauso durch Negation der Maske erreicht werden, wodurch das Schreiben nur innerhalb des spezifizierten Blocks möglich ist:

void setFieldA(bool b);
{
    return (_data & ~maskAWrite) | ((b << aPos) & maskAWrite);
}

Les- und Wartbarkeit mit std.bitmanip

D bietet einen kompletten Werkzeugkoffer zur Erzeugung benutzerdefinierter Bitmanipulationen. Allerdings ist bitmanipulierender Code oft fehlerträchtig und schwer zu warten. std.bitmanip schafft hier Abhilfe, indem es (unter Zuhilfenahme von Mixins) erlaubt, wartbare, leicht lesbare Bitmanipulationen zu schreiben - ohne dabei Performance zu opfern.

Das Beispiel zeigt eine BitVector-Definition, die, obwohl sie nur X Bits benutzt, kaum von regulären Strukturen unterscheidbar ist.

std.bitmanip und core.bitop beinhalten weitere Helfer, die sehr hilfreich für Anwendungen sind, die die Anforderung eines geringen Speicherverbrauchs stellen.

Auffüllen und Ausrichtung

Der Compiler wird Variablen auffüllen (engl.: Padding), die eine Größe kleiner als das Speicherlayout des Betriebssystems (size_t.sizeof) besitzen, wie z.B. bool, byte oder char. Daher wird die Ausrichtung (engl.: Allignment) der Felder beginnend bei Bit 0 empfohlen.

Weiterführende Quellen

rdmd playground.d