2013-04-15 6 views
7

HintergrundWie schreiben Sie (portabel) umgekehrte Netzwerk-Byte-Reihenfolge?

Wenn Binärdateiformate entwerfen, ist es in der Regel empfohlen, ganze Zahlen im Netzwerk-Byte-Reihenfolge zu schreiben. Dafür gibt es Makros wie htonhl(). Aber für ein Format wie WAV wird eigentlich das Little-Endian-Format verwendet.

Frage

Wie schreiben Sie portably Little-Endian-Werte, unabhängig davon, ob die CPU Code läuft auf ist ein Big-Endian oder Little-Endian-Architektur? (Ideen: können die Standard-Makros ntohl() und htonl() „umgekehrt“ verwendet werden, irgendwie Oder sollte der Code nur Testlaufzeit, wenn es auf einem kleinen oder Big-Endian-CPU und wählen Sie den entsprechenden Codepfad läuft?)

So Die Frage geht nicht wirklich um Dateiformate, Dateiformate waren nur ein Beispiel. Es könnte irgendeine Art von Serialisierung sein, bei der ein kleines Endian "auf dem Draht" erforderlich ist, wie beispielsweise ein (häretisches) Netzwerkprotokoll.

+0

Sie haben es drei Fragen, aber nur zwei abheben. –

+0

@ user2096041, neu angeordnet, ist es klarer? –

+0

Um die Endianness des Host-Systems zu finden, können Sie diesem Link folgen: http://stackoverflow.com/questions/1001307/detecting-endianness-programmatical-in-ac-program –

Antwort

16

C bietet bereits eine Abstraktion über die Endian des Wirtes: die number & Dolch; oder int & Dolch ;.

Ausgabe in einem bestimmten endianness produzieren kann portabel durch nicht versuchen getan werden klug zu sein: einfach die Zahlen als Zahlen und verschiebt Verwendung Bit interpretieren jedes Byte zu extrahieren:

uint32_t value; 
uint8_t lolo = (value >> 0) & 0xFF; 
uint8_t lohi = (value >> 8) & 0xFF; 
uint8_t hilo = (value >> 16) & 0xFF; 
uint8_t hihi = (value >> 24) & 0xFF; 

Dann schreiben Sie nur die Bytes in Welche Reihenfolge auch immer du wünschst.

Wenn Sie Byte-Sequenzen mit einigen endianness als Eingabe nehmen, können Sie sie in der Endian des Wirtes rekonstruieren, indem wieder Zahlen mit Bit-Operationen Konstruktion:

uint32_t value = (hihi << 24) 
       | (hilo << 16) 
       | (lohi << 8) 
       | (lolo << 0); 

& Dolch; Nur die Repräsentationen von Zahlen als Bytefolgen haben Endianness; Zahlen (d. h. Mengen) nicht.

+2

'hihi',' lolo', diese sind die besten Namen, die ich gesehen habe. Was meinst du Mengen? –

+0

@AmigableClarkKant die abstrakte Idee einer Zahl, im Gegensatz zu seiner Darstellung. –

+0

Ok, vielleicht solltest du stattdessen eine C++ Referenz für die Nummer verwenden. Wie auch immer, wie hilft es bei der Serialisierung und Endianz? Ich verstehe (und befürworte den Rest der Antwort) –

2

In der Tat funktioniert die MSDN ntohl() und htonl() sind die Inverse zusammen:

Die htonl Funktion wandelt eine u_long von Host zu TCP/IP Netzwerk-Byte Ordnung (das ist Big-Endian).

Die Ntohl-Funktion konvertiert eine u_long von TCP/IP-Netzwerk Reihenfolge zu Host Byte-Reihenfolge (die Little-Endian auf Intel-Prozessoren ist).

Ja, runtime detecting endianness ist eine sehr vernünftige Sache zu tun, und was im Grunde jede ready-to-use-Makro/Funktion sowieso irgendwann tun würde.

Und wenn Sie Little-Big-Endian-Konvertierungen selbst tun möchten, finden Sie unter Antwort von @ R-Martinho-Fernandes.

+2

Ich stimme nicht zu, dass das Erkennen von Endianness eine sehr vernünftige Sache ist. Das solltest du normalerweise nicht wissen müssen. Wenn Sie nicht performance-kritischen Code schreiben, ist es besser, endian-unabhängigen Code zu schreiben. – jamesdlin

+1

@jamesdlin Wenn ein crossplatform-Prozess geschrieben wird, kann derselbe Code auf Little-Endian- oder Big-Endian-Hardware ausgeführt werden. Wenn der Prozess über ein Netzwerk oder Dateien kommuniziert, möchten Sie, dass sie dieselbe Sprache sprechen. Der Code kann die Endianz zur Laufzeit prüfen und die Ausgabe/Eingabe entsprechend anpassen. –

+0

Das ergibt keinen Sinn. Wenn Sie richtigen endian-agnostischen Code schreiben, ist Ihr Code weiterhin plattformübergreifend und muss keine Laufzeitüberprüfungen (oder Kompilierungszeitprüfungen) durchführen. Wenn Sie beispielsweise Folgendes tun: 'fputc ((x >> 24) & 0xFF, fp)' wobei 'x 'eine 32-Bit-Ganzzahl ist, werden Sie unabhängig von der Plattform immer das höchstwertige Byte ausgeben. Siehe R. Martinho Fernandes 'Antwort. – jamesdlin

4

Hier ist eine Vorlage basierte Version:

#include <iostream> 
#include <iomanip> 

enum endianness_t { 
    BIG,   // 0x44332211 => 0x44 0x33 0x22 0x11 
    LITTLE,  // 0x44332211 => 0x11 0x22 0x33 0x44 
    UNKNOWN 
}; 

const uint32_t test_value = 0x44332211; 
const bool is_little_endian = (((char *)&test_value)[0] == 0x11) && (((char *)&test_value)[1] == 0x22); 
const bool is_big_endian  = (((char *)&test_value)[0] == 0x44) && (((char *)&test_value)[1] == 0x33); 

const endianness_t endianness = 
    is_big_endian ? BIG: 
    (is_little_endian ? LITTLE : UNKNOWN); 


template <typename T> 
T identity(T v){ 
    return v; 
} 

// 16 bits values ------ 

uint16_t swap_(uint16_t v){ 
    return ((v & 0xFF) << 8) | ((v & 0xFF00) >> 8); 
} 

// 32 bits values ------ 

uint32_t swap_(uint32_t v){ 
    return ((v & 0xFF) << 24) | ((v & 0xFF00) << 8) | ((v & 0xFF0000) >> 8) | ((v & 0xFF000000) >> 24); 
} 

template <typename T, endianness_t HOST, endianness_t REMOTE> 
struct en_swap{ 
    static T conv(T v){ 
    return swap_(v); 
    } 
}; 

template <typename T> 
struct en_swap<T, BIG, BIG>{ 
    static T conv(T v){ 
    return v; 
    } 
}; 

template <typename T> 
struct en_swap<T, LITTLE, LITTLE> { 
    static T conv(T v){ 
    return v; 
    } 
}; 

template <typename T> 
T to_big(T v) { 

    switch (endianness){ 
    case LITTLE : 
    return en_swap<T,LITTLE,BIG>::conv(v); 
    case BIG : 
    return en_swap<T,BIG,BIG>::conv(v); 
    } 
} 

template <typename T> 
T to_little(T v) { 
    switch (endianness){ 
    case LITTLE : 
    return en_swap<T,LITTLE,LITTLE>::conv(v); 
    case BIG : 
    return en_swap<T,BIG,LITTLE>::conv(v); 
    } 
} 


int main(){ 

    using namespace std; 

    uint32_t x = 0x0ABCDEF0; 
    uint32_t y = to_big(x); 
    uint32_t z = to_little(x); 

    cout << hex << setw(8) << setfill('0') << x << " " << y << " " << setw(8) << setfill('0') << z << endl; 

}