2009-04-07 18 views
7

Ich muss eine Bitfeldstruktur von Little-Endian zu Big-Endia-Architektur konvertieren. Was ist der beste Weg, dies zu tun, da es Probleme in Byte-Grenzen geben wird, wenn ich einfach die Strukturelemente vertausche.Konvertieren von Endianess auf einer Bitfeldstruktur

Ex Struktur ist:

struct { 
    unsigned int b1:1; 
    unsigned int b2:8; 
    unsigned int b3:7; 
    unsigned int b4:8; 
    unsigned int b5:7; 
    unsigned int b6:1; 
}; 
+0

Ihre Frage war genug, um meine Frage zu etwas getrennt zu beantworten - danke! :) – Cyrus

Antwort

-3

Um das zu erreichen, habe ich endlich eine Lösung gefunden (etwas, das von der obigen Lösung von epatel abgeleitet wurde). Dies ist, wenn ich von x86 zu Solaris SPARC umwandeln.

Wir müssen zuerst die eingehende Struktur tauschen und dann die Elemente in umgekehrter Reihenfolge lesen. Nachdem ich gesehen habe, wie die Strukturen ausgerichtet sind, habe ich gesehen, dass sich die Endianität sowohl in der Byte-Reihenfolge als auch in der Bit-Reihenfolge geändert hat. Hier ist ein Pseudocode.

struct orig 
{  
    unsigned int b1:1; 
    unsigned int b2:8; 
    unsigned int b3:7; 
    unsigned int b4:8; 
    unsigned int b5:7; 
    unsigned int b6:1; 
}; 

struct temp 
{  
    unsigned int b6:1; 
    unsigned int b5:7; 
    unsigned int b4:8; 
    unsigned int b3:7; 
    unsigned int b2:8; 
    unsigned int b1:1; 
}temp; 


func (struct orig *toconvert) 
{ 
    struct temp temp_val; 
    //Swap the bytes 
    swap32byte((u32*)toconvert); 
    //Now read the structure in reverse order - bytes have been swapped 
    (u32*)&temp_val = (u32 *)toconvert; 
    //Write it back to orignal structure 
    toconvert->b6=temp_val.b6; 
    toconvert->b5=temp_val.b5; 
    toconvert->b4=temp_val.b4; 
    toconvert->b3=temp_val.b3; 
    toconvert->b2=temp_val.b2; 
    toconvert->b1=temp_val.b1; 

}

Nach einigem Experimentieren fand ich, dass dieser Ansatz nur dann gültig ist, wenn die Elemente die Struktur vollständig füllen, das heißt es gibt keine ungenutzten Bits.

+0

Ich bin mir sicher, dass dies abgelehnt wird, weil es eine schlechte Idee ist, aber ich bin auf dieser Seite gelandet, weil ich mit Bitfeldern nicht vertraut bin und ich weiß nicht, warum es eine schlechte Idee ist. Könnte jemand bitte erklären? – Bear

1

Sie haben zwei 16-Bit-Abschnitte gibt (die ersten drei Felder und die letzten drei Felder sind 16 Bit).

Das sind nur 65536 Einträge. So haben Sie eine Nachschlagetabelle, die die Bit-umgekehrte Version der Felder enthält. Umbrechen Sie die Struktur in eine Union mit einer anderen Struktur, die zwei 16-Bit-Felder hat, um dies zu erleichtern?

So etwas (nicht getestet, ich bin nicht in der Nähe eines C-Compiler):

union u { 
    struct { 
     unsigned int b1:1; 
     unsigned int b2:8; 
     unsigned int b3:7; 
     unsigned int b4:8; 
     unsigned int b5:7; 
     unsigned int b6:1; 
    } bits; 
    struct { 
     uint16 first; 
     uint16 second; 
    } words 
} ; 

unit16 lookup[65536]; 

/* swap architectures */ 

void swapbits (union u *p) 
{ 
    p->words.first = lookup[p->words.first]; 
    p->words.second = lookup[p->words.second]; 
} 

Population der Lookup-Tabelle links als Übung für den Leser :)

jedoch Compiler doc lesen vorsichtig. Ich bin mir nicht sicher, ob der C-Standard erfordert, dass diese Struktur in ein Wort passt (obwohl ich davon ausgehen würde, dass die meisten Compiler das tun).

+2

Wenn die Leistung nicht absolut kritisch ist, verschwendet dieser Code 128k Speicher. Kein Wunder, dass 4Gb für produktive Arbeit nicht mehr ausreichen ;-) – MaxVT

+0

Nun, vielleicht ist es kritisch, wir haben es nicht erfahren. –

8

Sie könnten eine 32-Bit-Ganzzahl verwenden und Informationen daraus mithilfe von and- und Bitshift-Operatoren extrahieren. Damit können Sie einfach htonl (host-to-network, long) verwenden. Netzwerk-Byte-Reihenfolge ist Big Endian.

Dies ist nicht so elegant wie ein Bit-Feld, aber zumindest werden Sie wissen, was Sie haben und müssen sich keine Sorgen über den Compiler Polsterung Ihrer Strukturen machen.

+0

+1 Für mich ist htonl() oder htons() kombiniert mit Bitmasken und Bit-Shifts der wartungsfreundlichste Ansatz für diese Art von Sachen. – mouviciel

+0

Ja, Sie haben Recht, obwohl die Methode des Epatels auch funktioniert, ich muss nur sehen, wo alles nicht funktioniert :) – foo

+0

Die von epatel angegebene Methode ist ebenfalls sehr verbreitet (und ich habe sie ebenfalls hochgestuft). Es kann jedoch schwierig sein, wenn Bitfelder eine Bytegrenze überlappen. – mouviciel

5

In einem Projekt Portierung Code von MIPS zu Linux/x86 haben wir das getan.

struct { 

#ifdef __ONE_ENDIANESS__ 
    unsigned int b1:1; 
    unsigned int b2:8; 
    unsigned int b3:7; 
    unsigned int b4:8; 
    unsigned int b5:7; 
    unsigned int b6:1; 
#define _STRUCT_FILLED 
#endif /* __ONE_ENDIANESS__ */ 

#ifdef __OTHER_ENDIANESS__ 
    unsigned int b6:1; 
    unsigned int b5:7; 
    unsigned int b4:8; 
    unsigned int b3:7; 
    unsigned int b2:8; 
    unsigned int b1:1; 
#define _STRUCT_FILLED 
#endif /* __OTHER_ENDIANESS__ */ 

}; 

#ifndef _STRUCT_FILLED 
# error Endianess uncertain for struct 
#else 
# undef _STRUCT_FILLED 
#endif /* _STRUCT_FILLED */ 

Die Makros __ONE_ENDIANESS__ und __OTHER_ENDIANESS__ war angemessen für den Compiler müssen Sie möglicherweise verwendeten wir so aussehen, in die für Sie geeignet ist ...

+1

Beachten Sie, dass die Felder b2 und b5 im ersten Beispiel mehr als ein Byte umfassen, daher ist es unwahrscheinlich, dass sie im zweiten Fall erneut geschrieben werden können. Ansonsten kann dieser Trick viel Haarziehen ersparen. – RBerteig

+0

Nicht? Ich denke, wenn sizeof (int) hat es gut für uns gearbeitet ... – epatel

+0

ah;) sah jetzt ... Ich war nur editieren ohne zu schauen ... zu reparieren! – epatel

0

Es sollte die Bytes tauschen genug sein. Die Bitposition innerhalb eines Bytes ist bei großen und kleinen Endianen gleich.
z.B. :

char* dest = (char*)&yourstruct; 
unsigned int orig = yourstruct; 
char* origbytes = (char*)&orig; 
dest[0] = origbytes[3]; 
dest[1] = origbytes[2]; 
dest[2] = origbytes[1]; 
dest[3] = origbytes[0]; 
+0

Soweit ich sagen kann, spezifiziert der ANSI C-Standard nicht die Reihenfolge, in der Bitfelder innerhalb eines Bytes (oder Wortes) zugewiesen werden, so dass das Austauschen von Bytes möglicherweise nicht genug ist. –

+0

ja, wird nicht tragbar sein. aber ich denke * die meisten Compiler sollten die Bits in den "natürlichen" Platz stellen (zB struct {unsigned char a: 1, b: 6, c: 1} ---> ein Bit 0, b Bit 1-6, c Bit 7.) ... wenn die Portabilität im besten Fall ist, den Rat von Roe verwenden. – qwerty

1

Sie möchten dies zwischen dem Kanal (Datei oder Netzwerk) und Ihrer Struktur tun. Meine bevorzugte Vorgehensweise besteht darin, Datei-E/A von Strukturen durch Schreibcode zu isolieren, der die Dateipuffer in einer bekannten Darstellung aufbaut und den Lesecode abgleicht, der diese Umwandlung umkehrt.

Ihr spezielles Beispiel ist besonders schwer zu erraten, da die Bitfelder als unsigned int definiert sind und sizeof(unsigned int) besonders nicht portierbar ist.

Unter der Annahme, dass ein SWAG sizeof(int)==4 dann ein Zeiger auf die Struktur und die einzelnen Bytes erhalten Sie wahrscheinlich die Antwort, die Sie wollen.

Der Trick der Definition der Struktur unterschiedlich für verschiedene Plattformen Macht Arbeit, aber in dem Beispiel zu nennen Sie es an Bytegrenzen kein sauberer Bruch, so dass es wahrscheinlich nicht möglich sein, ein Äquivalent von einem zu produzieren Plattform in der anderen, ohne eines oder mehrere der Felder in zwei Teile zu teilen.

0

Sie sollten keine Bitfelder verwenden, wenn das physische Layout wichtig ist, da es in der Implementierung definiert ist, in welcher Reihenfolge das größere Wort ausgefüllt wird.

+0

oft Paket-Header (wo physikalische Layout extrem wichtig ist) verwenden Bitfelder, um Sub-Byte-Felder zu definieren. Nehmen wir zum Beispiel den IP-Header des Linux-Kernels in netinet/ip.h (oder http://lxr.linux.no/linux+v2.6.38/include/linux/ip.h#L80) – jbenet

+0

Ähm, na und? Sie codieren einen bestimmten Compiler (gcc). – zvrba

+0

Dies beantwortet die Frage des OP nicht; Das OP hat dies berücksichtigt. – Qix

6

Die Prozessor-Endianz hängt nicht mit der Reihenfolge der Bitfelder zusammen. Es ist durchaus möglich, dass zwei Compiler auf demselben Computer die umgekehrte Reihenfolge für Bitfelder verwenden. Also, da dies:

union { 
    unsigned char x; 
    struct { 
     unsigned char b1 : 1; 
     unsigned char b2 : 7; 
    }; 
} abc; 
abc.x = 0; 
abc.b1 = 1; 
printf("%02x\n", abc.x); 

Sofern Sie eine ausführliche Dokumentation zu haben, passieren, die einzige Möglichkeit, zu wissen, ob das Ausdrucken wird 01 oder 80 ist, es zu versuchen.

Verwandte Themen