2016-10-20 3 views
0

Je voulais suivre les directives de Rob Pikes et stocker des entiers sur le disque sans se soucier de l'endianess. Donc, voici mon code de test:sérialiser ints à big endian pour le stockage

#include <stdio.h> 
#include <stdlib.h> 
#include <inttypes.h> 
#include <string.h> 

typedef uint8_t byte; 

uint32_t _wireto32(byte *data) { 
    uint32_t i = 
    ((uint32_t)data[3]<<0) | 
    ((uint32_t)data[2]<<8) | 
    ((uint32_t)data[1]<<16) | 
    ((uint32_t)data[0]<<24); 
    return i; 
} 

void _32towire(uint32_t i, byte *data) { 
    data[0] = (i >> 24) & 0xFF; 
    data[1] = (i >> 16) & 0xFF; 
    data[2] = (i >> 8) & 0xFF; 
    data[3] = i  & 0xFF; 
} 

void _dump(char *n, byte *d, size_t s, uint64_t N) { 
    int l = strlen(n) + 9; 
    fprintf(stderr, "%s (len: %ld, Num: %ld): ", n, s, N); 
    size_t i; 
    int c; 
    for (i=0; i<s; ++i) { 
    fprintf(stderr, "%02x", d[i]); 
    if(i % 36 == 35 && i > 0) { 
     fprintf(stderr, "\n"); 
     for(c=0; c<l; ++c) 
     fprintf(stderr, " "); 
    } 
    } 
    fprintf(stderr, "\n"); 
} 

int main(int argc, char **argv) { 
    FILE *fd = NULL; 
    uint32_t n_orig = 20160809; 
    uint8_t b[4]; 
    uint32_t n_new; 

    if(argc != 2) { 
    fprintf(stderr, "Usage: util w|r\n"); 
    exit(1); 
    } 

    switch(argv[1][0]) { 
    case 'w': 
     if((fd = fopen("buffer.b", "wb+")) == NULL) { 
     perror("unable to write buffer.b"); 
     return 1; 
     } 

     _32towire(n_orig, b); 

     fwrite(b, 4, 1, fd); 
     close(fd); 

     _dump("out", b, 4, n_orig); 

     break; 

    case 'r': 
     if((fd = fopen("buffer.b", "rb+")) == NULL) { 
     perror("unable to open read buffer.b"); 
     return 1; 
     } 

     if((fread(b, 1, 4, fd)) <=0) { 
     perror("unable to read from buffer.b"); 
     return 1; 
     } 

     close(fd); 

     n_new = _wireto32(b); 

     _dump(" in", b, 4, n_new); 

    } 

    return 0; 
} 

Quand je lance ce sur un système x86, tout semble bien:

% ./util w && ./util r 
out (len: 4, Num: 20160809): 0133a129 
in (len: 4, Num: 20160809): 0133a129 

Maintenant, si je transfère le fichier de sortie à un système big-endian (aix sur powerpc dans mon cas), je reçois:

./util r 
in (len: 4, Num: 0): 0133a129 

Donc, je suis évidemment sur quelque chose. est-ce que quelqu'un a une idée?

Merci, Tom

+1

"Je suis évidemment négliger quelque chose" - pourquoi ?? a l'air ok pour moi. –

+0

Qu'est-ce qui ne va pas? Veuillez éditer votre question et indiquer clairement quelle sortie vous attendez. Tout me semble bien ici. –

+0

Que dit le débogueur? – Olaf

Répondre

2

Si vous vous demandez pourquoi les grands tirages système endian Num: 0, alors c'est à cause de votre fonction _dump prenant uint64_t N et non 32 bits. Sur une grosse machine endian, les 4 octets les plus significatifs sont 0.

+0

Oui, je l'ai trouvé moi-même il y a quelques minutes. Merci quand même! – Tom

+0

Merci, je l'ai trouvé moi aussi. – Tom

0

Bien que vous ayez une erreur entre fclose() et close() à:

$ gcc tst.c -otst 
tst.c: In function ‘main’: 
tst.c:61:7: warning: implicit declaration of function ‘close’ [-Wimplicit-function-declaration] 
     close(fd); 
    ^

Il fonctionne très bien dans AIX compilation avec GCC v4.8.3, même avec cette erreur et cet avertissement. Peut-être que cette version de gcc sait comment faire face à la coulée de uint32_t à uint64_t.

$ ./tst w 
out (len: 4, Num: 0): 0133a129 
$ ./tst r 
    in (len: 4, Num: 0): 0133a129 

Juste pour savoir, essayez un casting explicite:

_dump(" in", b, 4, n_new); 
_dump(" in", b, 4, (uint64_t)n_new); 

Mais, votre fonction _dump() pourrait être fixé pour recevoir le type compatible:

void _dump(char *n, byte *d, size_t s, uint32_t N) { 
... 
} 

Juste pour montrer la powerpc (AIX) quand j'ai testé:

Pour afficher la endianess:

#include <stdio.h> 

int main() { 
    int n = 0x01; 

    if (((char*)(&n))[0] == 1 && ((char*)(&n))[sizeof(n) - 1] == 0) { 
     printf("This is a litte-endian plataform\n"); 
    } else if (((char*)(&n))[0] == 0 && ((char*)(&n))[sizeof(n) - 1] == 1) { 
     printf("This is a big-endian plataform\n"); 
    } else { 
     printf("Couldn't determine the endianess of this plataform\n"); 
    } 
} 

Course à pied:

$ ./endianess 
This is a big-endian plataform 
$ uname -a 
AIX ???? 1 7 ???????????? powerpc AIX 
2

gcc -maix32 dit:

tom.c:27:3: warning: format '%ld' expects argument of type 'long int', 
but argument 5 has type 'uint64_t' [-Wformat=] 
fprintf(stderr, "%s (len: %ld, Num: %ld): ", n, s, N); 

donc faire ceci:

fprintf(stderr, "%s (len: %ld, Num: %ld): ", n, s, (long)N); 

Lors de la compilation toujours utiliser cette drapeaux: -W -Wextra -Werror -pedantic