Nils Hasenbanck ist der Gründer der Tsukisoft GmbH und ein Senior Developer. Seine Leidenschaft ist das Bauen von technisch eleganten, …
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.