Déterminer l'endianness du système?

Version imprimable

Citation:


Problème de [codeur de ]compilateur, pas de codeur d'application.
  • 25/07/2006, 22h23
    Pragmateek
    Et c'est portable?
    Si "unsigned long" fait plus de 4 byte?

    Code:

    1
    2
    3
    for(unsigned i=sizeof(long)-1;i>=0;i--){
    buf[12 + (sizeof(long)-1)+i] = (data >> (8 * i)) & 0xFF;
    }

    ?
  • 25/07/2006, 22h33
    Emmanuel Delahaye
    Citation:

    Envoyé par seriousme
    Et c'est portable ?

    Ben oui.
    Citation:

    Si "unsigned long" fait plus de 4 byte?
    On s'en fiche. Les poids forts > 32 bits sont ignorés. C'est toi qui a fait une boucle vaseuse, pas moi.

    Les formats des données réseau sont spécifiées. C'est pas comme en C où les tailles des types sont variables. Si la données fait 32 bits, c'est 4 octets. Point. Evidemment, en C, pour être portable on prend le type de taille >= qui va bien. (ici, unsigned long)

    Il faut décoreller dans sa tête le format réseau qui est indépendant de l'architecture et du langage, de l'implémentation.
  • 25/07/2006, 22h42
    Pragmateek
    Il faut juste veiller à ne pas utiliser de données trop grande qui pourrait tenir sur un "long" de 8 byte par exemple?
  • 25/07/2006, 22h55
    Emmanuel Delahaye
    Citation:

    Envoyé par seriousme
    Il faut juste veiller à ne pas utiliser de données trop grande qui pourrait tenir sur un "long" de 8 byte par exemple?

    Encore une fois, les données réseau sont spécifiées. Tu ouvres une RFC, y'a pas de mystères.

    Après, oui, il faut avoir un cerveau en état de marche et connaitre par coeur les tailles minimales garanties de chaque types C et utiliser le bon. Penser aussi à être en unsigned pour éviter les embrouilles.

    Un long de 8 bytes, c'est pas portable (et un byte, c'est quoi tu veux dire 8 x 8 = 64 bits ?).

    Un unsigned long long, c'est OK : 64 bits minimum.
  • 25/07/2006, 23h03
    Pragmateek
    Si sur une implémentation un type fait "n" byte et que ce même type sur le réseau doit tenir sur "m"<"n" byte il faut bricoler pour le transmettre.
    Il serait plus logique que les normes réseaux surdimensionnent la taille des différents types pour tous les accepter; ce qui risquerait bien sûr d'augmenter souvent inutilement la quantité de byte à transmettre.
  • 25/07/2006, 23h18
    David.Schris
    Citation:

    Envoyé par seriousme
    Pour 4 byte... mais c'est vrai que pour le principe:

    Non, ce n'est pas juste "pour le principe" : c'est pour prendre une bonne habitude. Si tu prends l'habitude de toujours libérer la mémoire que tu réserves, tu n'oublieras jamais de le faire (oui, je sais, cela semble assez évident)...ça peut sauver des vies...

    Citation:

    Envoyé par seriousme
    Et existe il une constante dans un header de GCC qui l'indiquerait?

    Je ne sais pas ce que tu appelles un "header de gcc".
    Par contre, chez moi (et chez pas mal d'autres), "ctype.h" inclue <endian.h> qui définit "__LITTLE_ENDIAN", "__BIG_ENDIAN", "__PDP_ENDIAN" (sans "__" sous BSD) et inclue <bits/endian.h> qui définit "__BYTE_ORDER" pour ma plateforme.
    Donc, je pourrai faire :
    Code:

    1
    2
    3
    4
    5
    6
    7
    8
    #include <endian.h>
    #if __BYTE_ORDER == __LITTLE_ENDIAN
    /* blabla */
    #elif __BYTE_ORDER == __BIG_ENDIAN
    /* bleble */
    #else
    /* blibli */
    #endif

    ...ce qui nous montre que tu n'as pas dû chercher dans tes "headers" avant de demander...
    (et puis, niveau portabilité, j'ai un doute...quelqu'un confirme ?)

    Sinon, concernant le reste de ton code :
    • "enum { /* blabla */ };" ==> là, c'est personnel, je n'aime pas les "enum" tous seuls, je préfère les "typedef enum { /* blabla */ } t_UnNomParlant;" car (en relisant mon code) ma fonction ne retournera pas juste un "int" anonyme et car les noms donnés aux valeurs pourront plus facilement être identifiés (en regardant le type de la variable à laquelle je les affecte) ;
    • "LITTLE_ENDIAN, BIG_ENDIAN, }" ==> elle sert à quoi la virgule à la fin ?
    • "if(p_c==NULL){" ==> quelques espaces ne feraient pas de mal ("if ( p_c == NULL ) {") ;
    • "p_c[0]=0xFFFF;" ==> "warning: large integer implicitly truncated to unsigned type" ;
    • "int* p_i=(int*)p_c;" ==> "parse error before `int'" (déclarations ==> en début de bloc !!! Sinon, fais du VB) ;
    • "return *p_i==255?LITTLE_ENDIAN:BIG_ENDIAN;" ==> "`p_i' undeclared" (cf. ci-dessus) ;
    • le déjà mentionné free() manquant (et le calloc inutile car, comme tu dis, "pour 4 bytes"...).



    Citation:

    Envoyé par Emmanuel Delahaye
    Problème de [codeur de ]compilateur, pas de codeur.

    Sauf que tout le monde n'est pas "codeur de compilateur" donc si il y a "problème", c'est le codeur (pas "de compilateur") qui devra le régler.
    Un petit exemple... Prends un bout de code de la librairie OpenSSL comme http://cvs.openssl.org/getfile/opens...5-ia64.S?v=1.3 (pris au hasard). Tu y trouveras par exemple des "#ifdef HOST_IS_BIG_ENDIAN". Ne me dis pas "ce n'est pas du C, c'est de l'assembleur" car tu ne feras qu'abonder dans mon sens (enfin...en partie) : si c'est codé en assembleur, c'est bien parce-que ce n'était pas qu'un problème de compilateur (ou en tous cas que le "problème" n'est pas résolu). Tu vois où je veux/voulais en venir ?
    Mais, à part en crypto (avec des besoins en termes de performance), j'ai rarement vu, et là c'est moi qui vais dans ton sens, de réel besoin de tester l'indianité...encore moins en C.

    Citation:

    Envoyé par seriousme
    Si sur une implémentation un type fait "n" byte et que ce même type sur le réseau doit tenir sur "m"<"n" byte il faut bricoler pour le transmettre.

    Tu dis "ce même type", c'est là que tu te crées un problème où il n'y en a pas : cela ne doit pas être vu comme un "même type" (d'un côté c'est du C sur une architecture donnée, de l'autre c'est un protocole (commun à plusieurs architectures)).
    Autrement dit, et comme l'a écrit Emmanuel, le problème (pour le codeur) est seulement de "connaitre par coeur les tailles minimales garanties de chaque types C et utiliser le bon" en fonction du protocole. Le problème pour celui qui définit le protocole est de bien choisir les tailles de données à transmettre sans se fixer sur une architecture mais en se basant sur les besoins de ce qu'il y a à transmettre. Quand ces besoins peuvent changer, il y a moyen de définir (dans le protocole) des types "à taille variable" (exemple classique : transmettre une chaine en unicode) ou de transmettre la version du protocole utilisé pour pouvoir le faire évoluer.
    Je ne sais pas si j'ai été très clair...
  • 26/07/2006, 09h16
    Médinoc
    Connaitre (dynamiquement ou non) l'endianness d'un système, ça peut être utile dès que tu veux sauvegarder des fichiers binaires et que tu n'as pas envie de dépendre de la bibliothèque où se trouvent les htons/ntohs/htonl/ntohl...

    Sous Windows, on dépend de wsock32.lib+dll ou de ws2_32.lib+dll, et je ne sais même pas si on a besoin ou non d'un appel à WSAStartup() pour les utiliser. Si c'est le cas, faire ses propres fonctions peut s'avérer un gain de maintenabilité du code...

    Edit: Je viens de tester, le WSAStartup() est inutile, il suffit d'être lié à la bibliothèque pour avoir accès à ces fonctions. Ainsi, connaitre l'endianness n'a que peu d'importance...
  • 26/07/2006, 10h47
    David.Schris
    Citation:

    Envoyé par Médinoc
    Connaitre (dynamiquement ou non) l'endianness d'un système, ça peut être utile dès que tu veux sauvegarder des fichiers binaires et que tu n'as pas envie de dépendre de la bibliothèque où se trouvent les htons/ntohs/htonl/ntohl...

    Vois le bout de code d'Emmanuel :
    Code:

    1
    2
    3
    4
    5
    6
    7
    8
    9
     
       unsigned char buf[123] = {0};
       unsigned long data = 0x1234;
     
       /* offset arbitraire */
       buf[12 + 0] = (data >> (8 * 3)) & 0xFF; /* MSB  = 00 */
       buf[12 + 1] = (data >> (8 * 2)) & 0xFF; /*         = 00 */
       buf[12 + 2] = (data >> (8 * 1)) & 0xFF; /*         = 12 */
       buf[12 + 3] = (data >> (8 * 0)) & 0xFF; /* LSB   = 34*/

    Il ne dépend d'aucune bibliothèque :D
  • 26/07/2006, 10h54
    Médinoc
    Oui, mais c'est plus long.
    Généralement, je suis plutôt pour une structure avec des tailles de champs et de bourrage bien définies (types spécifiant la taille genre uint32_t, pragma pack/attribute packed, etc). Et une sauvegarde en gros de la structure.
  • 26/07/2006, 18h44
    Pragmateek
    Citation:

    A quoi ça sert ?
    Justement puisque les transmission réseaux sont en big endian:
    Code:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    if(BIG_ENDIAN){
    /*transmission sans transformation des donnees*/
    }
    else{
    /*formatage des donnees*/
    /* offset arbitraire */
       buf[12 + 0] = (data >> (8 * 3)) & 0xFF; /* MSB  = 00 */
       buf[12 + 1] = (data >> (8 * 2)) & 0xFF; /*         = 00 */
       buf[12 + 2] = (data >> (8 * 1)) & 0xFF; /*         = 12 */
       buf[12 + 3] = (data >> (8 * 0)) & 0xFF; /* LSB   = 34*/
    }

    Et donc gain de temps si l'architecture est déjà big endian.
    Non?:mrgreen:
  • 26/07/2006, 18h56
    Emmanuel Delahaye
    Citation:

    Envoyé par seriousme
    Justement puisque les transmission réseaux sont en big endian:
    Code:

    1
    2
    if(BIG_ENDIAN){
    /*transmission sans transformation des donnees*/


    Ah bon ? En big endian, on ne met pas les données dans le buffer d'émission ?
    Citation:

    Code:

    1
    2
    3
     
    }
    else{


  • 26/07/2006, 19h03
    Pragmateek
    Citation:

    Ah bon ? En big endian, on ne met pas les données dans le buffer d'émission ?
    Oui mais directement, pas en quatre étapes et surtout sans utiliser les opérateurs bit à bit qui sont couteux:
    Code:

    snprintf(buf+12,4,"%ld",data);
    .
  • 26/07/2006, 19h28
    Emmanuel Delahaye
    Citation:

    Envoyé par seriousme
    Oui mais directement, pas en quatre étapes et surtout sans utiliser les opérateurs bit à bit qui sont couteux:
    Code:

    snprintf(buf+12,4,"%ld",data);
    .

    Rien à voir. On parlait de transferts binaires, pas de conversion en chaine (qui est insensible à l'endianness).
  • 26/07/2006, 20h24
    Pragmateek
    Ou alors avec une fonction de copie de zone mémoire.
    Mais le principe est il correct : on n'est pas obligé de ranger les byte?
  • 26/07/2006, 21h20
    Emmanuel Delahaye
    Citation:

    Envoyé par seriousme
    Ou alors avec une fonction de copie de zone mémoire.
    Mais le principe est il correct : on n'est pas obligé de ranger les byte?

    Si la représentation mémoire est exactement celle du réseau et qu'il n'y a pas de problèmes d'alignement, ça ira.

    Ce n'est pas trivial et ce n'est pas portable. J'ai proposé une méthode universelle. Est-elle vraiment couteuse ? Peut être... il faut faire des mesures...
  • 26/07/2006, 21h27
    Pragmateek
    Citation:

    la représentation mémoire
    L'endianness ou ça recouvre autre chose?
    Citation:

    ce n'est pas portable
    Si l'endianness du système est bien vérifiée et que la norme de transmission est respectée ça devrait être portable.
  • 26/07/2006, 21h31
    Emmanuel Delahaye
    Citation:

    Envoyé par seriousme
    L'endianness ou ça recouvre autre chose?

    La largeur de la donnée, par exemple, son alignement...
    Citation:

    Si l'endianness du système est bien vérifiée et que la norme de transmission est respectée ça devrait être portable.
    Du 'portable' à coups de 'si', c'est plus du portable...

    Enfin, si ça t'amuse...
  • 27/07/2006, 09h31
    Médinoc
    seriousme : C'est à cela que servent htons/htonl...

    Sur une architecture Big-Endian, il arrive qu'on trouve directement des macros #define htons(x) (x), donc aucune perte de performance...
  • 07/02/2007, 20h46
    jeroim
    Bonjour,

    voila pour un projet je doit faire une fonction qui recois un int et le retranscrit dans un fichier en une chaine de caractere. Seulement, et pour une question de portabilite il me faut connaitre l'endian de la machine sur laquelle je suis (sinon je risque une foi sur 2 de retranscrire mon entier a l'envers).

    J'ai par consequent fait quelques recherches avant de tomber sur ce topic et sur ce qui suit :

    Citation:

    Code:

    1
    2
    int x = 1;
    	if(*(char *) & x == 1)


    seulement je ne suis pas sur de bien comprendre ce if, et etant hors de question que je me serve d'un bout de code que je ne comprend pas je viens a vous pour vous demander quelques precisions:

    - nous avons donc un masque binaire, mais je m'etonne qu'il ne soit pas fait bit par bit, et surtout que l'on compare un int (sur 4 octets) a un char (sur 1 octet).
    - a quoi correspond la premiere etoile juste apres le if ?
    - et d'ou vient donc ce char ?

    Merci de bien vouloir repondre a tout ces questions certainement tres triviales pour vous mais qui pourraient m'etre d'une grande aide.
  • 07/02/2007, 21h15
    Emmanuel Delahaye
    Citation:

    Envoyé par jeroim
    voila pour un projet je doit faire une fonction qui recois un int et le retranscrit dans un fichier en une chaine de caractere.

    sprintf(), fprintf()...
    Citation:

    Seulement, et pour une question de portabilite il me faut connaitre l'endian de la machine sur laquelle je suis (sinon je risque une foi sur 2 de retranscrire mon entier a l'envers).
    Pas du tout. Quand c'est converti en texte, c'est auto-démerdant (au charset près, mais ASCII couvre 99% des cas). Et même si c'était en binaire (tableau de bytes), du moment que le format du fichier (ou du flux) est spécifié (généralement 'réseau', c'est à dire MSB en tête), on sait écrire du code portable indépendant de l'endiannes, à coup de tableau de unsigned char et d'opérateurs binaires &, |, >>, << etc.

    Donc, faux problème.
  • 08/02/2007, 01h32
    souviron34
    il y a un autre moyen dynamique :

    Là c'est appliqué à des ports, mais ça indique directement little ou big endian...

    Code:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    typedef union
    {
       int i ;
       char carac[4] ;
    }TestStruct ;
    
      test.i = 123 ;
      if ( test.carac[0] == '{' ){
        /* network byte order.
         * We change to host byte order
         * and return the value. 
         */
        return (ntohs(isp->s_port)) ;
      }
      else{    /* host byte order.
         * We simply returns the value. 
         */
        return (isp->s_port) ;
       
      }

    ça m'a été très utile pour faire communiquer des machines Unix(HPUX, Silicon Graphics Mips) et des Linux... car :

    Code:

    1
    2
    3
    4
    5
    6
    7
      * On Linux machine getservbyname
      * returns the port in network order while on unix 
      * machine it is not clear. Therefore we performe a test to check
      * the byte order for the local machine.
      * if the machine is is network byte order we use the
      * function ntohs (network to host) the change it.

  • 08/02/2007, 09h52
    David.Schris
    Citation:

    Envoyé par souviron34
    il y a un autre moyen dynamique : [...]

    Un autre ? A part la valeur de "i", quelle différence fondamentale avec ce que j'avais donné ici ?
  • 08/02/2007, 10h52
    jeroim
    Tout d'abbord merci pour vos reponses,

    Citation:

    Envoyé par Emmanuel Delahaye
    sprintf(), fprintf()...

    en fait je ne peu me servir que de write();
    voici ce que j'ai pour le moment, et on m'a genttilment indique que ca ne marcherai pas :
    Code:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
     
     
    /* les valeurs dans l'int sont en hexadecimal, */
    /* les calculs sont donc fait dans cette base */
     
    void	put_to_file(t_list_hexa *h, char *name)
    {
      char	*str;
      int	fd;
     
        /* ouverture du fichier et application des droits */
      fd = open(name, O_CREAT | O_RDWR | O_TRUNC, 0760);
      if (fd < 0) /* verification de la valeur de retour de open */
        {
          write(1, "Cannot open file\n", 18);
          exit(-1);
        }
      while (h != NULL) /* les donnees sont dans l'int hex dans cette liste chainee */
        {
          str = xmalloc(sizeof(char) * 2);
          str = put_to_char(h, str);
          write(fd, str, 2);
          free(str);
          h = h->next; /* incrementation de la liste chainee */
        }
      close(fd);
    }
     
    /* on ecrit dans le char * les caracteres un par un */
    char	*put_to_char(t_list_hexa *h, char *str)
    {
      if (h->hex < 10)
        {
          str[0] = '0';
          str[1] = put_to_char_sub(h->hex, str[1]);
        }
      else
        {
          str[1] = put_to_char_sub(h->hex % 10, str[1]);
          str[0] = put_to_char_sub(h->hex / 10, str[0]);
        }
      return (str);
    }
     
    char	put_to_char_sub(int hex, char str)
    {
      if (hex < a)
        str = hex + 30; /* on ajoute ce qu'il faut pour arriver */
      else
        str = hex + 57; /* a la valeur ascii du caractere hexa */
      return (str);
    }

    voila, moi ca me semblait tres simple comme vous pouvez le voir et j'ai eu un tres beau : "oui mais tu ne gere pas les differents endian comme ca" ...
  • 08/02/2007, 10h54
    souviron34
    ok désolé j'avais mal lu.. à cause du gros free en rouge !! :lol:
  • 08/02/2007, 11h25
    jeroim
    Et bien j'ai finalement compris ce fameux if qui permet de recuperer l'endian de la machine (une bonne nuit de sommeil de temps a autres fait du bien aux neurones ^^)

    je vais desormais regarder pour m'en servir a bon escient, merci encore, bonne continuation.
  • 08/02/2007, 16h27
    Emmanuel Delahaye
    Citation:

    Envoyé par jeroim
    en fait je ne peu me servir que de write();

    Alors il faut cesser de parler de portabilité.
    Citation:

    voici ce que j'ai pour le moment, et on m'a genttilment indique que ca ne marcherai pas :

    Code:

    /* les valeurs dans l'int sont en hexadecimal, */

    Ceci n'a aucun sens. Dans une variable il n'y a que des 0 ou des 1. C'est du binaire. Point.
    Citation:

    Code:

    /* les calculs sont donc fait dans cette base */

    What ? Quels calculs ?
    Citation:

    Code:

    str = xmalloc(sizeof(char) * 2);

    Ca sert à quoi d'allouer dynamiquement une tableau de taille fixe ? A m'énerver ? Accessoirement, sizeof(char) vaut 1. Toujours. (Normal, c'est l'unité de compte)

    "Combien y'a d'euros dans un euro ? Ben un, pourquoi ? "

    Alors dans un char, y'a un char. Voilà. Logique basique...
    Citation:

    Code:

    str = put_to_char(h, str);

    Va falloir qu'on m'explique à quoi sert cette fonction et surtout pourquoi elle modifie str...

    Ah ça vient ...
    Citation:

    /* on ecrit dans le char * les caracteres un par un */
    Ouch ! Tout ça pour faire une conversion num/texte en hexadécimal ? Tu devrais réfléchir au fait que les données sont codées en binaire et qu'il suffit de les traiter par groupe de 4 bits pour déterminer quelle valeur comprise entre 0 et F (0-15) on a. C'est très simple, et il n'y a aucun problème de portabilité dû à l'endianess.

    Je propose :

    int num2hex(char *s, int width, unsigned data);

    qui s'utilise comme ça :
    Code:

    1
    2
    3
    4
    5
    6
    7
    8
    9
     
       char chaine [10]; /* a l'aise */
       unsigned donnee = 128; /* 0-255 soit 00 a FF  d'ou une largeur de 2 */
       int largeur = 2;
       int err = num2hex (chaine, largeur, donnee);
     
       if (!err)
       {
           /* OK */

    a toi de coder la fonction. Je suggère que la chaine construite soit valide, c'est à dire terminée par un 0, ça peut aider au debug (printf)

    Avec 128, on doit obtenir "80".
  • 08/02/2007, 16h36
    Emmanuel Delahaye
    Citation:

    Envoyé par jeroim
    Et bien j'ai finalement compris ce fameux if qui permet de recuperer l'endian de la machine

    Il n'y a pas ce problème avec ce que tu veux faire. Tu perds ton temps...