Bitpacking Spaß 24. Januar 2023 | 2 minute Lesen

Bitpacking Spaß

Wenn man eine C-API in Rust portiert, ist es wichtig, die grundlegenden Strukturaufbauregeln zu verstehen, die der C-Compiler verwendet. Eine solche Regel ist die Verwendung von Bitpacking, bei der mehrere Felder in ein einzelnes Backing-Feld gepackt werden, um eine kompakte Speicherung von Daten zu ermöglichen.

Wir haben kürzlich eine C++-Bibliothek mit einer C-API in Rust portiert. Dort haben wir eine gepackte Struktur angetroffen, die so aussah:

struct BITPACKED {
    uint8_t FieldA : 8;
    uint64_t FieldB : 1;
    uint64_t FieldC : 7;
    uint64_t FieldD : 48;
};

Die Struktur BITPACKED enthält vier Felder: FieldA, FieldB, FieldC und FieldD. FieldA ist als 8-Bit-Ganzzahl ohne Vorzeichen definiert. FieldB, FieldC und FieldD sind alle als 64-Bit-Ganzzahl ohne Vorzeichen definiert.

Wie groß ist also diese Struktur? Meine ursprüngliche Vermutung war 8 Bytes, da der Compiler sie in ein 64-Bit-Backing-Feld packen könnte.

Aber laut C-Standard ist das Bitpacking von Implementierung zu Implementierung definiert, was bedeutet, dass der Compiler frei ist, die Felder im Speicher anzuordnen. GCC und Clang werden diese Struktur in ein 64-Bit-Packing-Feld packen. Aber unser Ziel, der Microsoft Visual C++ (MSVC) Compiler hat eine spezielle Regel für das Bitpacking von Strukturen, die den Leser überraschen könnte (es hat auch den Ersteller der C++-Bibliothek überrascht, welche beabsichtigten, die Struktur in ein 64-Bit-Feld zu packen).

MSVC wird nur Felder, die gleich groß sind, in dasselbe Backing-Feld packen. Das bedeutet, dass FieldA in ein 8-Bit-Backing-Feld und FieldB, FieldC und FieldD in ein anderes 64-Bit-Backing-Feld gespeichert werden. Da das zweite Feld auf 8 Bytes ausgerichtet sein muss, hat die resultierende Struktur eine Größe von 16 Bytes.

So sah die resultierende Struktur in Rust ungefähr so aus:

#[repr(C)]
pub struct BITPACKED {
    FieldA: u8,
    Packed: u64,
}

Zusammenfassend ist das Verstehen der Layoutregeln von bitgepackten Strukturen entscheidend beim Portieren einer C-API in Rust, da es die Größe und Ausrichtung der Daten im Speicher beeinflussen kann. Überprüfen Sie immer die Dokumentation des spezifischen Compilers, den Sie verwenden, da die Regeln für das Bitpacking zwischen Compilern variieren können.

Nils Hasenbanck

Nils Hasenbanck

Nils Hasenbanck ist der Gründer der Tsukisoft GmbH und ein Senior Developer. Seine Leidenschaft ist das Bauen von technisch eleganten, …