2009-04-07 9 views
7

Ho bisogno di convertire una struttura bit field da little-endian a architettura big-endia. Qual è il modo migliore per farlo, poiché ci saranno problemi nei limiti dei byte, se cambio semplicemente gli elementi della struttura.Conversione di endianess su una struttura di bit field

Es struttura è:

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

La tua domanda è stata sufficiente per rispondere alla mia domanda riguardante qualcosa di separato - grazie! :) – Cyrus

risposta

-3

Per ottenere questo risultato ho finalmente ottenuto una soluzione (alcuni dei quali derivano dalla soluzione di Epatel sopra). Questo è se io converto da x86 a Solaris SPARC.

Dobbiamo prima scambiare la struttura in entrata e quindi leggere gli elementi in ordine inverso. Fondamentalmente dopo aver visto come sono allineate le strutture, ho visto che l'endianess cambiava sia nell'ordinamento dei byte che nell'ordinamento dei bit. Ecco uno pseudo codice.

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; 

}

Dopo alcuni esperimenti ho trovato che questo approccio è valido solo se gli elementi riempiono completamente la struttura, cioè non vi sono bit inutilizzati.

+0

Sono sicuro che questo è downvoted perché è una cattiva idea, ma sono atterrato su questa pagina perché non ho familiarità con i campi bit e non so perché sia ​​una cattiva idea. Qualcuno potrebbe spiegare per favore? – Bear

1

si hanno due sezioni 16 bit là (i primi tre campi e gli ultimi tre campi sono 16 bit).

Sono solo 65536 voci. Quindi avere una tabella di ricerca che contiene la versione bit-invertita dei campi. Avvolgi la struttura in un'unione con un'altra struttura che ha due campi a 16 bit per semplificare la procedura?

Qualcosa di simile (non testata, io non sono in prossimità di un compilatore C):

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]; 
} 

Popolazione della tabella di ricerca lasciato come esercizio per il lettore :)

Tuttavia, leggere il tuo doc compilatore accuratamente. Non sono sicuro che lo standard C richieda che la struttura tenga conto di una parola (anche se mi aspetterei che molti compilatori lo facciano).

+2

A meno che le prestazioni siano assolutamente critiche, questo codice spreca 128k di memoria. Non c'è da stupirsi che 4 GB non siano più considerati sufficienti per un lavoro produttivo ;-) – MaxVT

+0

Beh, forse è fondamentale, non ci è stato detto. –

8

È possibile utilizzare un numero intero a 32 bit ed estrarre informazioni da esso utilizzando gli operatori and- e bitshift. Con quello in atto, potresti semplicemente usare htonl (da host a rete, a lungo). L'ordine dei byte di rete è big endian.

Questo non sarà elegante come un campo di bit, ma almeno saprai cosa hai e non dovrai preoccuparti del compilatore che riempie le tue strutture.

+0

+1 Per me, htonl() o htons() combinato con maschere di bit e bit shift è l'approccio più gestibile per questo tipo di cose. – mouviciel

+0

Sì, si è corretto, anche se il metodo di epatel come indicato di seguito funziona anche, ho solo bisogno di vedere dove tutto non funzionerà :) – foo

+0

Il metodo fornito da epatel è molto comune anche (e ho anche svitato). Ma può essere complicato quando i campi di bit si sovrappongono a un limite di byte. – mouviciel

5

In un progetto di porting di codice da MIPS a Linux/x86 ci è piaciuto.

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 */ 

Le macro __ONE_ENDIANESS__ e __OTHER_ENDIANESS__ era appropriato per il compilatore abbiamo usato quindi potrebbe essere necessario guardare in cui è appropriato per voi ...

+1

Si noti che i campi b2 e b5 si estendono su più di un byte nel primo esempio, quindi non è probabile che possano essere riscritti per corrispondere nel secondo caso. Altrimenti, questo trucco può far risparmiare un sacco di pettinature. – RBerteig

+0

Non? Penso che se sizeof (int) ha funzionato bene per noi ... – epatel

+0

ah;) visto ora ... Stavo solo modificando senza guardare ... aggiustando! – epatel

0

dovrebbe essere sufficiente per scambiare i byte. La posizione del bit all'interno di un byte è la stessa in big e little endian.
ad es. :

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

Per quanto posso dire, lo standard ANSI C non specifica l'ordine in cui i bitfield sono allocati all'interno di un byte (o parola), quindi scambiare byte potrebbe non essere sufficiente. –

+0

sì, non sarà portatile. ma io * credo * la maggior parte dei compilatori dovrebbe mettere i bit nel posto "naturale" (ad es. struct {unsigned char a: 1, b: 6, c: 1} ---> un bit 0, b bit 1-6, c bit 7). ... se la portabilità è in primo piano, utilizzare il consiglio di capriolo. – qwerty

1

Si desidera eseguire questa operazione tra il canale (file o rete) e la struttura. La mia pratica preferita è quella di isolare l'I/O del file dalle strutture tramite il codice di scrittura che crea i buffer di file in una rappresentazione conosciuta e il codice di lettura corrispondente che inverte tale trasformazione.

L'esempio specifico è particolarmente difficile da indovinare perché i bitfield sono definiti come unsigned int e sizeof(unsigned int) è particolarmente non portatile.

Supponendo che uno SWAG che sizeof(int)==4 quindi ottenere un puntatore alla struttura e regolare i singoli byte probabilmente ottiene la risposta desiderata.

Il trucco di definire lo struct in modo diverso per le diverse piattaforme forza lavoro, ma l'esempio che cita non c'è un taglio netto a limiti di byte, quindi non è probabile che sia possibile produrre un equivalente di uno piattaforma nell'altra senza dividere uno o più campi in due pezzi.

0

Non utilizzare i campi di bit quando il layout fisico è importante perché è definito dall'implementazione in cui viene popolata la parola più grande.

+0

Spesso le intestazioni dei pacchetti (dove il layout fisico è estremamente importante) utilizzano i campi di bit per definire i campi di sotto byte. Prendiamo ad esempio l'intestazione IP del kernel linux in netinet/ip.h (o http://lxr.linux.no/linux+v2.6.38/include/linux/ip.h#L80) – jbenet

+0

E allora, e allora? Stanno codificando per un compilatore specifico (gcc). – zvrba

+0

Questo non risponde alla domanda dell'OP; il PO avrà tenuto conto di questo. – Qix

6

L'endianness del processore non è correlata all'ordinamento dei campi dei bit. È possibile che due compilatori sullo stesso computer utilizzino l'ordine opposto per i bitfield. Quindi, dato questo:

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); 

A meno che non vi capita di avere una documentazione dettagliata, l'unico modo per sapere se che stamperà 01 o 80 è di provarlo.

Problemi correlati