2013-04-15 6 views
7

SfondoCome si scrive (portabilmente) in ordine inverso per byte di rete?

Durante la progettazione di formati di file binari, è generalmente consigliato di scrivere interi in ordine di byte di rete. Per questo, ci sono macro come htonhl(). Ma per un formato come WAV, in realtà viene utilizzato il formato little endian.

Domanda

Come si fa a scrivere i valori portabile little endian, a prescindere se la CPU il codice viene eseguito su un big endian o little endian architettura? (idee: possono le macro standard ntohl() e htonl() essere utilizzati "al contrario" in qualche modo o dovrebbe il codice appena runtime di prova se è in esecuzione su un piccolo o grande CPU endian e scegliere il percorso di codice appropriato??)

Così la domanda non è proprio sui formati di file, i formati di file erano solo un esempio. Potrebbe essere qualsiasi tipo di serializzazione in cui è richiesto il piccolo endian "on the wire", come un protocollo di rete (eretico).

+0

Hai tre domande lì, ma solo due distinguersi. –

+0

@ user2096041, riorganizzato, è più chiaro? –

+0

Per trovare il endianness del sistema host è possibile seguire questo link: http://stackoverflow.com/questions/1001307/detecting-endianness-programmatically-in-ac-programme –

risposta

16

C fornisce già un'astrazione su endianness dell'ospite: il number & pugnale; oppure int e pugnale ;.

Produrre uscita in un dato endianness può essere fatto portabile da non cercando di essere intelligente: semplicemente interpretare i numeri come numeri e l'uso po sposta per estrarre ogni byte:

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; 

Poi basta scrivere i byte in qualunque ordine desideri.

Quando si sta assumendo sequenze di byte con un po 'endianness come input, è possibile ricostruirli in endianness dell'ospite dal nuovo costruendo numeri con le operazioni di bit:

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

& pugnale; Solo le rappresentazioni di numeri come sequenze di byte hanno endianness; i numeri (cioè le quantità) no.

+2

'hihi',' lolo', quelli sono i migliori nomi che ho visto. Cosa intendi per quantità? –

+0

@AmigableClarkKant l'idea astratta di un numero, al contrario della sua rappresentazione. –

+0

Ok, forse dovresti collegare a qualche tipo di ref C++ per il numero. Ad ogni modo, come aiuta la serializzazione e l'endianness? Comprendo (e approvo il resto della risposta) –

2

Infatti, le funzioni MSDN ntohl() e htonl() sono l'inverso della vicenda:

La funzione htonl converte un u_long dall'host alla rete TCP/IP byte cassa (che è big-endian).

La funzione ntohl converte u_long dall'ordine di rete TCP/IP per ospitare l'ordine byte (che è little-endian su processori Intel).

Sì, runtime detecting endianness è una cosa molto sensata da fare, e in fondo quello che ogni macro/funzione pronta per l'uso avrebbe fatto a un certo punto comunque.

E se si desidera eseguire autonomamente piccole conversioni endian, consultare la risposta di @ R-Martinho-Fernandes.

+2

Non sono d'accordo sul fatto che rilevare l'endianità sia una cosa molto sensata da fare. Normalmente non dovresti aver bisogno di sapere. A meno che tu non stia scrivendo un codice critico per le prestazioni, scrivere un codice indipendente da endian è un'idea migliore. – jamesdlin

+1

@jamesdlin durante la scrittura di un processo crossplatform, lo stesso codice può essere eseguito su hardware little-endian o big-endian. Quando il processo comunica tramite rete o file, vorresti che parlino la stessa lingua. Il codice può controllare l'endianness in fase di esecuzione e accogliere di conseguenza l'output/input del processo. –

+0

Questo non ha senso. Se stai scrivendo il codice appropriato per l'endian-agnostico, il tuo codice è ancora multipiattaforma e non è necessario eseguire alcun controllo in fase di esecuzione (o in fase di compilazione). Ad esempio, se si fa: 'fputc ((x >> 24) e 0xFF, fp)' dove 'x' è un numero intero a 32 bit, si scriverà sempre il byte più significativo indipendentemente dalla piattaforma. Vedi la risposta di R. Martinho Fernandes. – jamesdlin

4

Ecco una versione basata modello:

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

} 
Problemi correlati