2016-04-15 3 views
7

Angesichts einer Binärdatei mit 32-Bit-Little-Endian-Felder, die ich analysieren muss, möchte ich Parsing-Code, der korrekt kompiliert unabhängig von Endianness der Maschine, die diesen Code ausführt schreiben. Zur Zeit verwende ichOptimale und portable Konvertierung von 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]; 
} 

dies jedoch eine optimale Montage zu generieren. Auf meinem Rechner g++ -O3 -S produziert:

_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 

, warum dies geschieht? Wie konnte ich es überzeugen optimalen Code zu erzeugen, wenn auf Little-Endian-Maschinen zusammengestellt:

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

, die ich durch Kompilieren bekommen haben:

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

Da ich weiß, dass meine Maschine Little-Endian, ich Sie wissen, dass die obige Assembly optimal ist, aber es wird fehlschlagen, wenn sie auf einer Big-Endian-Maschine kompiliert wird. Es verletzt auch strikte Aliasing-Regeln, wenn es inlined ist, könnte es UB sogar auf Little-Endian-Maschinen erzeugen. Gibt es einen gültigen Code, der zur optimalen Montage kompiliert wird wenn möglich?

Da ich davon ausgehe, dass meine Funktion viel inline ist, kommt jede Art von Runtime-Endian-Erkennung nicht in Frage. Die einzige Alternative zum Schreiben optimaler C/C++ - Codes besteht darin, die Kompilierzeit-Endianerkennung zu verwenden und template s oder #define s zu verwenden, um auf den ineffizienten Code zurückzugreifen, wenn das Zielendian nicht Little-Endian ist. Dies scheint jedoch sehr schwierig portabel zu sein.

+0

Sie können 'reinterpret_cast' nicht zuordnen. Es wird kein Byte neu angeordnet. Wenn Sie den Endian-Byte-Shuffle tanzen müssen, müssen Sie die Band bezahlen. – user4581301

+0

Die Sache ist, dass, wenn meine Compile-Ziel-Plattform Little Endian ist, dann brauche ich keine Byte-Shuffle - Compiler sollte das auch wissen, aber es erzeugt sowieso Byte-Shuffle-Code. –

+0

Thing ist der Compiler weiß nicht, dass Sie Endian spiegeln. Es sieht nur eine Reihe von Schichten und Ors. Wäre aber ein netter Trick. Kann auf der Makefile-Ebene abspielen und die richtige Funktion kompilieren und verknüpfen, aber das wird jegliche Inlining-Funktion beenden. – user4581301

Antwort

1

Diverse Plattformbibliotheken, die ich kenne, tun dies, indem sie Makros für die Endian-Swapping-Routinen definieren, basierend auf dem Wert von #define BIG_ENDIAN. In den Fällen, in denen die Quelle endianness Ihr Ziel endianness passt, können Sie einfach:

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

Zum Beispiel:

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

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

+0

'' scheint nicht tragbar zu sein. Siehe https://gcc.gnu.org/ml/gcc-help/2007-07/msg00342.html –

+0

Sie müssen hier wählen - optimal oder tragbar. @j_kubik hat eine portable Version, die nicht optimal ist. Verschiedene andere Antworten werden andere Techniken vorschlagen, die mehr oder weniger portabel oder optimal sind, aber der einzige Weg, um sicherzustellen, dass Sie eine Ausgabe erhalten, die im Do-Nothing-Fall nichts tut, ist die Verwendung des Präprozessors. Es gibt keine Garantien, dass irgendein gegebener C++ - Compiler den do-nothing-Fall anders erkennt. –

+0

Ich denke, eine Antwort besteht darin, zu versuchen, die Zielplattform zu erkennen, und wenn sie nicht erfolgreich ist, dann benutze suboptimalen Code als Fallback. –

2

kurze Antwort - Verwendung htonl - seine gonna optimiert werden up the wazzoo

+2

Das einzige Problem ist, dass Netzwerk Reihenfolge Big Endian ist. –

+0

yup und htonl wird das wissen und konvertieren oder nicht abhängig von der Maschine läuft auf – pm100

+0

Ich weiß das, aber 'htonl' und Freunde sind immer von/zu Maschine Endian zu/von Big Endian (Netzwerk Endian) zu konvertieren. Meine Datei ist definitionsgemäß little-endian, und ich brauche einen Funktionssatz, der von/in den Endian der Maschine zu/von Little Endian konvertiert. Es gibt keine Möglichkeit zu sehen, dass ich "htonl" oder "ntohl" verwenden könnte, um mein Problem zu lösen, außer vielleicht, um immer zu Big-Endian zu konvertieren und dann immer irgendwie ein Byte-Shuffling durchzuführen. Dies ist wahrscheinlich nicht annähernd optimal. –

Verwandte Themen