2016-04-15 12 views
7

Dato un file binario con campi little-endian a 32 bit che ho bisogno di analizzare, voglio scrivere il codice di analisi che compila correttamente indipendentemente da endianness della macchina che esegue quel codice. Attualmente io usoConversione ottimale e portatile di endian in c/C++

uint32_t fromLittleEndian(const char* data){ 
    return uint32_t(data[3]) << (CHAR_BIT*3) | 
     uint32_t(data[2]) << (CHAR_BIT*2) | 
     uint32_t(data[1]) << CHAR_BIT | 
     data[0]; 
} 

questo, tuttavia generare assembly inoptimal. Sulla mia macchina g++ -O3 -S produce:

_Z16fromLittleEndianPKc: 
.LFB4: 
    .cfi_startproc 
    movsbl 3(%rdi), %eax 
    sall $24, %eax 
    movl %eax, %edx 
    movsbl 2(%rdi), %eax 
    sall $16, %eax 
    orl %edx, %eax 
    movsbl (%rdi), %edx 
    orl %edx, %eax 
    movsbl 1(%rdi), %edx 
    sall $8, %edx 
    orl %edx, %eax 
    ret 
    .cfi_endproc 

perché sta succedendo questo? Come potevo convincerlo a produrre codice ottimale quando compilato su macchine little endian:

_Z17fromLittleEndian2PKc: 
.LFB5: 
    .cfi_startproc 
    movl (%rdi), %eax 
    ret 
    .cfi_endproc 

che ho ottenuto dalla compilazione:

uint32_t fromLittleEndian2(const char* data){ 
    return *reinterpret_cast<const uint32_t*>(data); 
} 

Dal momento che so che la mia macchina è little-endian, I sappiate che sopra l'assemblaggio è ottimale, ma fallirà se compilato su una macchina big-endian. Inoltre viola le regole di aliasing rigoroso, quindi se inline potrebbe produrre UB anche su macchine little endian. Esiste un codice valido che verrà compilato nell'assemblaggio ottimale se possibile?

Poiché mi aspetto che la mia funzione sia molto inclinata, qualsiasi tipo di rilevamento di endian runtime è fuori questione. L'unica alternativa alla scrittura di codice C/C++ ottimale consiste nell'utilizzare il rilevamento del tempo di compilazione endian e utilizzare template s o #define s per ricorrere al codice inefficiente se il target endian non è little-endian. Questo tuttavia sembra essere piuttosto difficile da fare portabilmente.

+0

Non è possibile abbinare 'reinterpret_cast'. Non sta eseguendo alcun riordino dei byte. Se devi ballare l'endian byte shuffle, devi pagare la band. – user4581301

+0

Il fatto è che se la mia piattaforma di destinazione di compilazione è little endian allora non ho bisogno di byte shuffle - anche il compilatore dovrebbe saperlo, ma produce comunque codice di shuffle byte. –

+0

La cosa è che il compilatore non sa che stai sfogliando endian. Vede solo un mucchio di turni e or. Sarebbe un bel trucco da avere, però. Potrebbe giocare a livello makefile e compilare e collegare nella funzione corretta, ma questo ucciderà qualsiasi inlining. – user4581301

risposta

1

Diverse librerie di piattaforme che conosco fanno ciò #definendo i macro per le routine di scambio degli endian, in base al valore di #define BIG_ENDIAN. Nei casi in cui l'endianness fonte soddisfa le vostre endianness destinazione, è possibile solo:

#ifdef LITTLE_ENDIAN 
    #define fromLittleEndian(x) (x) 
#else 
    #define fromLittleEndian(x) _actuallySwapLittle((x)) 
#endif 

Ad esempio:

http://man7.org/linux/man-pages/man3/endian.3.html

http://fxr.watson.org/fxr/source/sys/endian.h

+0

'' non sembra essere portatile. Vedi https://gcc.gnu.org/ml/gcc-help/2007-07/msg00342.html –

+0

Devi scegliere qui - ottimale o portatile. @j_kubik ha una versione portatile non ottimale. Varie altre risposte suggeriranno altre tecniche che sono più o meno portatili o ottimali, ma l'unico modo per assicurarsi di ottenere un risultato che non faccia nulla nel caso di non-fare è usare il preprocessore. Non ci sono garanzie che qualsiasi compilatore C++ riconoscerà altrimenti il ​​caso do-nothing. –

+0

Suppongo che una risposta sia tentare di rilevare la piattaforma di destinazione e, in caso di esito negativo, utilizzare il codice suboptimale come fall-back. –

2

breve risposta - uso htonl - la sua gonna essere ottimizzato up the wazzoo

+2

L'unico problema è che l'ordine di rete è big endian. –

+0

yup e htonl lo sapranno e convertiremo o meno a seconda della macchina è in esecuzione su – pm100

+0

Lo so, ma 'htonl' e gli amici stanno sempre convertendo da/a machine endian a/da big endian (network endian). Il mio file è per definizione little-endian, e ho bisogno di un set di fuction che converta da/a machine endian a/from little endian. In nessun modo vedo che potrei usare 'htonl' o' ntohl' per risolvere il mio problema, eccetto forse per convertire sempre in big endian e poi fare sempre qualche shuffling di byte comunque. Questo non è probabile che sia vicino all'ottimale. –

Problemi correlati