Ricerca tra la vecchia roba

Byte ordering

Posted: Giugno 26th, 2007 | Author: | Filed under: Programmazione | Commenti disabilitati su Byte ordering

Oltre le varie incompatibilità fra i computer esiste quella relativa all'ordinamento dei byte nei tipi di dati che hanno bisogno di più di un byte per essere gestiti: i cosidetto byte più significativi saranno i primi o gli ultimi?

Per capirci: facciamo finta che si possa memorizzare in una cella di memoria una cifra (un numero) per volta, quindi il numero 25 avrà bisogno di due caselle, ma all'interno di un nostro ipotetico computer esistono a loro volta due possibilità per immagazzinarle: o scrivo 2 5 oppure 5 2 e non è cosa da poco! Sento qualcuno mormorare "ma si! chi se ne frega!!! tanto all'interno del sistema operativo non se ne accorge nessuno!!! le chiamate di sistema di I/O leggeranno e scriveranno tutte allo stesso modo", parzialmente vero, se non fosse che esiste una fonte di I/O gigantesca a cui tutti ci colleghiamo che ci impone la necessità di (eventualmente) convertire i dati e le informazioni dal suo formato al nostro (se questi sono differenti ovviamente). Ormai avrete capito che sto parlando di Internet: nella rete i dati viaggiano secondo il cosidetto network byte ordering, corrispondente al Big Endian dei computer, in cui il byte più significativo è memorizzato prima al contrario dei sistemi little endian in cui ovviamente avviene il contrario.

Nello standard POSIX dei socket esistono delle funzioni che trasformano automaticamente certe grandezze numeriche in C (ma presumo esistano anche in altri linguaggi) dal formato della macchina su cui gira il sistema (detto host) al formato delle rete: sono definite nell'header arpa/inet.h e sono 

uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);

mi pare li usi anche winzoz con i suoi WinSock.

Ho scritto un programma di esempio per visualizzare tramite cifre esadecimali cosa succede nella conversione fra una versione e l'altra (funziona solo se avete un processore little endian, intel per capirci!)

/*
 *     *-byteorder.c-*
 *
 *     gcc -W -Wall -pedantic byteorder.c -o byteorder
 *
 *     Per capire se il computer è big/little endian
 *
 *     Utile anche per capire la conversione fra sistema
 *     decimale/binario/esadecimale
 *
 *     1byte = 8bit
 *                 = 2^8 valori possibili
 *                 = 256 possibili valori
 *                 = (2^4)^2
 *                 = 16^2
 *                 = 2 cifre esadecimali
 *
 *     cosa abbastanza utile dal punto di vista visuale per capire la
 *     storia dell'ordinamento dei byte.
 *
 *     Nel caso di questo programma si usa uno short int (guarda caso la
 *     stessa situa del numero delle porte TCP/IP) che equivale a 2 bytes
 *     cioé a 4 cifre esadecimali.
 *
 *     N.B: Ricordarsi che due byte vogliono dire 16 bit e quindi 65536
 *     valori diversi, ma che si inizia a contare da 0, quindi il range
 *     va da 0 (2^0) a 65535 (2^16-1)!!!
 *     
 *     N.B: Ricordarsi che se non specificato unsigned l'ultimo byte
 *     viene tenuto per il segno, limitando quindi l'output a soli 15 byte!!!
 *
 *
 *     Eventualmente per avere un output pulito fare redirezione su
 *     stderr
 *
 *         ./byteorder.c [numero] 2>/dev/null
 */
#include<stdio.h>
#include<stdlib.h>
#include<arpa/inet.h>

#define dummy_value 255

int main(int argc,char* argv[]){
    /*stringa per la formattazione del numero*/
    char cifre_esadecimali[]="%%0%dx";
    short unsigned int dummy;
    /*usage*/
    fprintf(
            stderr ,
            "Programma per mostrare la differenza nell'ordinamento dei byte "
            "fra un sistema little endian e l'ordinamento utilizzato nelle comunicazioni attraverso la rete "
            "utilizzando come elemento di conversione uno unsigned short int che ha dimensione di %d byten",sizeof(short int));    

    if(argc<2){
        fprintf(stderr , "Senza opzioni usa %d come valore di conversionen",dummy_value);
        dummy = dummy_value;
    }else
        dummy = (short int)atoi(argv[1]);

    /*generiamo il numero di cifre esadecimali necessarie*/
    sprintf(cifre_esadecimali , cifre_esadecimali,2*sizeof(short int));

    /*(cifra) cifra esadecimale – cifra esadecimale convertita*/
    fprintf(stdout,"(%d) ",dummy);
    fprintf(stdout , cifre_esadecimali,dummy);
    fprintf( stdout," – " );
    fprintf(stdout , cifre_esadecimali,htons(dummy));
    fprintf(stdout, "n");

    return EXIT_SUCCESS;
}

 Per vedere cosa succede arrivati a 65355 fate così

 for(( i=$((2**16-10)) ; i<$((2**16+10)) ; i++ )); do ./byteorder $i 2>/dev/null ;done

Piccola chicca di programmazione che in questo caso non serve a niente: nel caso che in esadecimale la cifra sia del tipo ff00, viene convertita in 00ff ma stampata tramite la printf come ff siccome i byte nulli vengono tralasciati; sempre con la printf è possibile impostare il numero di cifre che devono essere visualizzate (al limite quelle mancanti vengono sostituite con degli zeri) tramite la formatting string (info libc "output conversion syntax") definita in questo caso come "%4x" (2 byte sono 4 cifre esadecimali): nel codice sopra ho utilizzato

 sprintf(cifre_esadecimali , cifre_esadecimali,2*sizeof(short int));

che a runtime decide il numero di cifre da visualizzare… quasi autoreferenziale… "%%%dx" viene trasformato in "%4x", se qualcuno ha voglia scriva una funzione che prenda come argomento il numero di cifre da stampare (oltre che l'oggetto da stampare)…

Per ulteriori informazioni man byteorder


Comments are closed.